vol.45 すぐに使える!アコーディオンメニューの実装

f:id:BOEL:20160517165409j:plain

こんにちは。エンジニアの富澤です。
今回はWebページでよくみかけるアコーディオンメニューについて勉強しましたのでご紹介します。
アコーディオンメニューは、限られた範囲で多数の項目を一覧したいときに便利です。
※本TIPSではHTMLとCSSの基本的な知識があることを前提とします。
また、jQueryも使用しているためこちら(jQueryサイト)からソースをダウンロードするかgoogleなどが配信しているCDNサービス(https://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js)を利用し、HTMLに読み込んでください。

 

シンプルな実装

シンプルなアコーディオンメニューを実装していきます。

html

<div class="a_container">
<div class="a_item">demo</div>
<div class="a_contents">
<p>ダミーダミーダミー</p>
<p>ダミーダミーダミー</p>
<p>ダミーダミーダミー</p>
</div>
</div>

css

.a_container{
width:500px;
margin:50px auto;
}
.a_item{
background:#70f9a1;
color:#fff;
text-align:center;
padding:10px;
cursor:pointer;
}
.a_contents{
display:none;
padding:10px;
height:110px;
border:solid 1px #d6dddf;
}
.a_contents p{
margin:10px 0;
border-bottom:dashed 1px #d6dddf;
}


jQuery

$(document).ready(function(){
$(".a_item").click(function(){
$(this).next().slideToggle(300);
});
});


class名「.a_contents」を「display:none;」に設定し、アコーディオンメニューのコンテンツ部分を非表示にしておきます。
class名「.a_item」に対しクリックによって発動するイベント「click(function(){});」を、その中に「slideToggle()」を設定し、クリックした時にスライドしながらコンテンツ部分が開閉するようにします。

 

ワンポイント

.click(function(){});
クリックした時に処理を実行するイベントを登録できます。特定のclass名、id名に対して、設定をすることができます。ここではclass名「.a_item」がクリックされたときにイベントを発生させます。

 

$(this).next().slideToggle(300);
「this」はイベントを登録している要素に対し、そのアクションを行った要素のみに有効です。class名などを指定すると、「そのclassがあたっているすべての要素」に対して発動してしまいますが、「this」にすると押したボタンに対してのみにイベントが有効になります。

 

「next()」は、「次」にあたる兄弟要素を指します。ここでは、クリックしたボタンの次の要素のコンテンツ部分(「.a_contents」)を指します。

「slideToggle()」は要素が非表示の時はslideDownを行い、要素が表示している時はslideUpを行います。()内には時間を指定することができます。「slideToggle(300)」の場合、アニメーションに0.3秒の時間をかけて動作します。

 

リッチな実装

シンプルなアコーディオンメニューを元に機能を増やし、リッチな実装にしていきます。
増やしていく機能を下記にまとめました。

 

・マウスオーバーしたとき、色を変化させる
・クリックしたとき、選択中のものに色をつける
・一方が開いていて、開いていない方のアコーディオンをクリックしたとき、
 開いているアコーディオンを閉じる
・クリックし、選択したものの本文を遅れてフェードインさせる
・右側の矢印に開閉が分かるようなアニメーションをつける


html

<div class="a_container">
<div class="a_item_r">demo</div>
<div class="a_contents_r">
<p>ダミーダミーダミー</p>
<p>ダミーダミーダミー</p>
<p>ダミーダミーダミー</p>
</div>
<div class="a_item_r">demo</div>
<div class="a_contents_r">
<p>ダミーダミーダミー</p>
<p>ダミーダミーダミー</p>
<p>ダミーダミーダミー</p>
</div>
</div>

 

css

.a_item_r{
background:#70f9a1;
color:#fff; text-align:center;
padding:10px; cursor:pointer;
position: relative
}
.a_contents_r{
display:none;
padding:10px;
height:110px;
border:solid 1px #d6dddf;
}
.a_item_r:hover{
background-color:#6aec99;
}
.selected{
background-color:#ffc44d;
}
.a_contents_r p{
display:none;
margin:10px 0;
border-bottom:dashed 1px #d6dddf;
}
.a_item_r:after{
background:url(arrow_dw.png) no-repeat left top;
content: "";
display: block;
width: 20px;
height: 12px;
position:
absolute;
right: 20px;
background-size: 100% auto;
top:15px;
bottom:0;
-webkit-transition: 0.3s linear;
-moz-transition:0.3s linear;
-ms-transition:0.3s linear;
transition: 0.3s linear;
}
.open_close_r:after{
-webkit-transform: rotateZ(180deg);
transform: rotateZ(180deg);
}


ポイント1 擬似要素「:hover」

指定することでマウスオーバーしたとき、CSSに変化を与えることができます。
ここでは「.a_item_r:hover」にbackground-colorを指定することで「.a_item_r」をマウスオーバーしたとき、色に変化をさせることができます。

 

ポイント2 擬似要素「:after」

要素の末尾に要素を追加し、CSSでデザインすることができます。
ここでは右側の矢印を「:after」で挿入しています。「:after」を使うときcontent:""を指定するのがポイントです。

 

ポイント3 「transiton」

時間的変化をまとめて指定することができます。ここでは「transition: 0.3s linear;」(0.3秒間を一定のスピードで変化)を指定しています。 これにより上記ソースにある「transform」のスピードを調整しています。

 

ポイント4 「transform」

要素に対して回転させたりする動きを適用する際に使用します。ここでは「.open_close_r:after」に「transform: rotateZ(180deg);」を指定することで、アコーディオンメニューが開いた時に180度回転する設定をしています。

 

※「transition」「transform」は、CSS3から新しく登場したプロパティで記述の際、各ブラウザ用の"ベンダープレフィックス"が必要になります。
-webkit-transform : Safari,Google Chromeなど
-moz-transform : Mozila Firefox
-ms-transform : Internet Explorer

 

 

$(document).ready(function(){
$(".a_item_r").click(function(){
$(".a_item_r").removeClass("open_close_r");
$(".a_contents_r p").css("display","none");
$(this).next().slideUp(300);
$(this).removeClass("selected");
if($(this).next().css("display")=="none"){
$(this).addClass("open_close_r");
$(".a_contents_r").slideUp(300);
$(this).next().slideDown(300);
$("+.a_contents_r p",this).fadeIn(1500);
$(".selected").removeClass("selected");
$(this).addClass("selected");
}
});
});


class名「.a_item_r」に対しクリックによって発動するイベント「click(function(){});」を設定します。


その中に、2回目以降の動作を正しく実行するよう
「$(".a_item_r").removeClass("open_close_r");」→「.a_item_r」から「.open_close_r」を消去
「$(".a_contents_r p").css("display","none");」→「.a_contents_r p」を非表示にする
「$(this).next().slideUp(300);」→クリックした要素の次の要素に対しslideUpする
を設定しておきます。

 

次に、ifについてです。if構文は「条件分岐」といいます。
「if($(this).next().css("display")=="none")」は、もし「this」の次の兄弟要素が非表示ならば、何らかの処理を実行する、という意味になります。
何らかの処理に値する部分はif( 条件式 ) { 何らかの処理 }中括弧部分に記述します。

 

$(this).addClass("open_close_r"); 「open_close_r」を追加する
$(".a_contents_r").slideUp(300); 「.a_contents_r」をスライドアップする
$(this).next().slideDown(300); 「this」の次の兄弟要素をスライドダウンさせる
$("+.a_contents_r p",this).fadeIn(1500); 「"+.a_contents_r p",this」をフェードインする
$(".selected").removeClass("selected"); 「.selected」に対し「.selected」を削除する
$(this).addClass("selected"); 「this」に対し「selected」を追加する

 

・マウスオーバーしたとき、色を変化させる

.a_item_r:hover{background-color:#6aec99;}
background-colorを指定することで「.a_item_r」をマウスオーバーしたとき、色に変化をさせることができます。

 

・クリックしたとき、選択中のものに色をつける

$(this).removeClass("selected");

$(this).addClass("selected");
「removeClass」は指定したclassの削除をします。「addClass」は指定したclassの追加をします。
ここではマウスオーバーしたときのために入っています。

 

・一方が開いていて、開いていない方のアコーディオンをクリックしたとき、開いているアコーディオンを閉じる

$(".a_contents_r").slideUp(300);

$(this).next().slideDown(300);
最初に開いているアコーディオンを閉じる処理をし、次にクリックしたアコーディオンを開きます。

 

・クリックし、選択したものの本文を遅れてフェードインさせる

$("+.a_contents_r p",this).fadeIn(1500);
「fadeIn()」プロパティをつかって、非表示の要素をフェードインさせます。
ここでは、アコーディオンメニューが開いたときに文字がフェードインするようにしています。「.fadeIn(1500)」の場合、アニメーションに1.5秒の時間をかけて動作します。

 

・右側の矢印に開閉が分かるようなアニメーションをつける

$(".a_item_r").removeClass("open_close_r");

$(this).addClass("open_close_r");
「.open_close_r」が矢印のアニメーションに必要な設定になっています。
そのため、「removeClass」と「addClass」を用いてclassの削除と追加をします。
アニメーションの設定はCSSで設定しています。

 

ワンポイント

if( /* 条件式 */ ){
/* 何らかの処理 */
}else{
/* 別の何らかの処理 */
}
if内の要素が真である場合に処理を行います。偽の場合、else{}内の処理を行います。


CSS3での実装

これまではHTML+CSS+jQueryで実装を行いました。実はCSSのみでの実装も可能なのです。
CSSのみで実装を行うためにはフォームの構成部品であるチェックボックスのON・OFFを利用します。

 

html

<div class="a_container">
<label for="Panel1">demo<label>
<input type="checkbox" id="Panel1" class="open_close_c"/>
<ul>
<li>ダミーダミーダミー<li>
<li>ダミーダミーダミー<li>
<li>ダミーダミーダミー<li>
<ul>
<div>

 

css

label {
display: block;
background-color: #70f9a1;
color: #fff; padding: 10px;
text-align:center;
}
label:hover{
background-color:#6aec99;
}
input[type="checkbox"].open_close_c{
display: none;
}
ul{
border:solid 1px #d6dddf;
-webkit-transition: all 0.3s;
-moz-transition: all 0.3s;
-ms-transition: all 0.3s;
transition: all 0.3s;
margin: 0;
padding: 0;
list-style: none;
}
li{
margin:10px 0;
color:#666666;
border-bottom:dashed 1px #d6dddf;
}
input[type="checkbox"].open_close_c + ul{
height: 0;
overflow: hidden;
}
input[type="checkbox"].open_close_c:checked + ul{
height: 110px;
padding:10px;
}


class名「.open_close_c」に「display:none;」を設定し、チェックボックス自体は非表示にします。「ul」に対してアニメーションが発生したときのスピードを、「transition」で設定します。


「ul」を非表示にする際には「display」での非表示をつかわず、hightをつかいます。
「transition」のアニメーションがdisplayに対して無効のため、アニメーションにできないからです。


設定は「height:0;」「overflow:hidden;」とします。
チェックボックスをチェックしたとき、「height:auto;」を設定することで、「hight」が0から110pxになる過程が、アニメーションするようになります。
非表示のチェックボックスをチェックするような動作をさせるために「lavel」を利用します。


「input type="checkbox"」に対して、idを設定しておきます。
「label」タグ内にforを設定します。「for」は「input type="checkbox"」で設定したid名と同様にしておきます。
この設定をすることにより、「label」をクリックとチェックボックスのON・OFFが連動するようになります。


これによって、
「input[type="checkbox"].open_close_c + ul」の場合(チェックされていない場合)と
「input[type="checkbox"].open_close_c:checked + ul」の場合(チェックした場合)で
CSSを分けて書くことができます。

 

ワンポイント

input[type="checkbox"].open_close_c:checked + ul


「input[type="checkbox"」は、属性を使ったCSSの指定方法です。属性名や属性値によりスタイルの適用を指定することができます。
「input[type="checkbox"].open_close_c:checked + ul」は、クラス名「.open_close_c」を持つ「input[type="checkbox"」の次の兄弟要素である「ul」対してCSSをかけるための記述です。

 

まとめ

いかがでしたでしょうか?
アコーディオンメニューは、「click」イベントを用いて対象に「class」を追加することで様々な実装をすることができます。しかし、jQueryはソースを上から読み込んでいくため、記述は正しくても順番が違うと実装できないことがあり苦労しました。またCSS3の登場でjQueryを使わなくても実装可能になりました。今後も新しい実装方法が出てくるかも知れません。Web業界の進歩はとても早いので遅れをとらぬよう、日々勉強していきます。