Javascriptでアコーディオンを実装する
開閉の動作をJavascriptでやるパターンです。
Jqueryを利用すると、アコーディオン自体は3行で実装できます。
<dl>
<dt class="header">ヘッダー<span>▽</span></dt>
<dd>
<p>私は先刻すでにこの著作方について事の日を使うでた。はたして今朝を説明性もほとんどどんな批評でないかもが取りつかれからいるましには発会やりだでて、全くには死んうましでです。</p>
<p>先がなりな事はよし当時で引続きだたなかっ。しきりに岩崎さんに招待作物始終お尋ねを乗ったろこの生徒私か鑑定へというご教育たなましだと、その場合は私か畸形考でもたて、大森さんのものに壁の彼らにほとんどご落第と致さてあなた人真似をお病気にしようにようやくご真似にあれないたば、そんなにひょろひょろ講演に云っなけれて来です事にしたいじ。</p>
<p>場合をはいかに行くがすまましますでませから、いやしくもけっして考えて病気はいっそ広いなけれ事ん。</p>
</dd>
<dt class="header">ヘッダー<span>▽</span></dt>
<dd>
<p>私は先刻すでにこの著作方について事の日を使うでた。はたして今朝を説明性もほとんどどんな批評でないかもが取りつかれからいるましには発会やりだでて、全くには死んうましでです。</p>
</dd>
</dl>
$('.header').on('click',function(){
$(this).next().slideToggle(300, 'swing');
});
slideToggle() で開閉してくれます。
今回のところは、htmlをdl要素で書いていますが、特にhtmlの書き方に制限はありません。htmlに合わせて、slideToggleさせるセレクタを指定してあげれば大丈夫。
アイコン
ヘッダー横に開閉状態を示すアイコンとして「▽」を置いています。
装飾的なものなので、できれば ::after 疑似要素などで配置したいところですが、Jqueryでは疑似要素を操作できません。
なので、ヘッダー要素のクラスを操作する…のは後述するとして、ここはjsでやってみます。
アイコンを span 要素としてhtmlに置いて、クリックのたびに、これを180度回転させることにします。
var $angle = 0;
$('.header').on('click',function(){
$angle = $angle + 180;
$(this).find('span').css({transform: 'rotate(' + $angle + 'deg)'});
});
できるだけ簡単に!! と思ったんですけど、if文とか使ったほうがいいのかもしれない……
初期状態
開閉する要素を、最初は閉じた状態にしておきたいので、下記を追加します。
$('dd').hide();
Javascriptで処理しておくと、Javascriptが効かない環境で「見えないコンテンツ」が生まれてしまう問題を回避できます。
いうて現代、Javascriptが効かない環境は、ほぼほぼないわけですけども。
読み込み時にチラッと閉じておく要素が見えてしまうのがネックです。
気になる場合は、ロード画面を用意することになる…かな…??
実装に向いてるケース
まとめると、Javascriptは下記のようになります。
$(document).ready(function(){
$('dd').hide();
var $angle = 0;
$('.header').on('click',function(){
$angle = $angle + 180;
$(this).next().slideToggle(300, 'swing');
$(this).find('span').css({transform: 'rotate(' + $angle + 'deg)'});
});
});
詳細は後述するんですが、開閉の処理をcssだけで実装するには、開閉する要素の高さを想定する必要があります。
Javascriptなら、この高さを気にしなくてよいので、コンテンツ量がまったく読めないときは、この方法をとるといいのかなと思います。
前述のアイコンや初期表示については、適宜cssで実装するとよいかと。
最低限のJavascriptでアコーディオンを実装する
クラスの付け外しだけJavascriptで、あとはcssで実装するパターンです。
<dl>
<dt>ヘッダー</dt>
<dd>
<p>私は先刻すでにこの著作方について事の日を使うでた。はたして今朝を説明性もほとんどどんな批評でないかもが取りつかれからいるましには発会やりだでて、全くには死んうましでです。</p>
<p>先がなりな事はよし当時で引続きだたなかっ。しきりに岩崎さんに招待作物始終お尋ねを乗ったろこの生徒私か鑑定へというご教育たなましだと、その場合は私か畸形考でもたて、大森さんのものに壁の彼らにほとんどご落第と致さてあなた人真似をお病気にしようにようやくご真似にあれないたば、そんなにひょろひょろ講演に云っなけれて来です事にしたいじ。</p>
<p>場合をはいかに行くがすまましますでませから、いやしくもけっして考えて病気はいっそ広いなけれ事ん。</p>
</dd>
<dt>ヘッダー</dt>
<dd>
<p>私は先刻すでにこの著作方について事の日を使うでた。はたして今朝を説明性もほとんどどんな批評でないかもが取りつかれからいるましには発会やりだでて、全くには死んうましでです。</p>
</dd>
</dl>
$('dt').on('click',function(){
$(this).toggleClass('open');
});
dt::after {
content: "▽";
transition: all .3s;
}
dd {
max-height: 0;
overflow: hidden;
transition: all .3s;
}
dt.open::after {
transform: rotate(180deg);
}
dt.open + dd {
max-height: 1000px;
}
動作に係るcssだけ引っこ抜きました。
できるだけJavascriptでやろうとした前項と違って、開閉状態を示すアイコンは ::after 疑似要素にしておけます。
初期状態では開閉コンテンツを閉じておきたいですが、cssで処理してしまっても、Javascriptが効かない環境はほぼほぼないので問題ないでしょう。
高さを指定できない要素のcssアニメーション
要素の開閉というと、表示する高さをアニメーションさせたいものです。
実数で指定できれば問題ないのですが、height: auto; はアニメーション(transition)が効きません。
そこで、max-height で、コンテンツが全部見えるであろう高さを指定するわけですが、
コンテンツが間違いなく見えることを重視して、9999pxなどの大きすぎる数値を指定してしまうと、それはそれで問題が出てきます。
試しにアニメーション速度を0.3秒、max-height を 9999px としてみました。
開くときは一見問題なさそうですが、指定したよりアニメーションが速いです。また、閉じるときは、ボタンクリックからしばらく反応しません。
アニメーション速度に指定した「0.3秒」は、目に見えるコンテンツの高さではなく、あくまでmax-height の9999px を0.3秒で変化させているからです。
max-height に指定する値は、100vh(1画面ぶん)や、1000pxであれば、あまり気にならないように思います。ケースバイケースですけども…
どうしても開閉するコンテンツの量を想定できないときは、Javascriptで高さを取得することになりそうです。
実装に向いてるケース
個人的には、もっともよく使う方法です。
例ではクリックする要素を toggleClass させていますが、ここを適宜変更すると、いろいろなhtmlの書き方に対応できます。
開閉する要素のコンテンツ量がそこそこコンパクトにおさまるなら、実装、メンテナンスとも簡単です。
cssだけでアコーディオンを実装する
Javascriptを使わないパターンです。
開閉については前項同様、max-height でアニメーションさせます。
<dl>
<input id="check1" type="checkbox">
<dt><label for="check1">ヘッダー</label></dt>
<dd>
<p>私は先刻すでにこの著作方について事の日を使うでた。はたして今朝を説明性もほとんどどんな批評でないかもが取りつかれからいるましには発会やりだでて、全くには死んうましでです。</p>
<p>先がなりな事はよし当時で引続きだたなかっ。しきりに岩崎さんに招待作物始終お尋ねを乗ったろこの生徒私か鑑定へというご教育たなましだと、その場合は私か畸形考でもたて、大森さんのものに壁の彼らにほとんどご落第と致さてあなた人真似をお病気にしようにようやくご真似にあれないたば、そんなにひょろひょろ講演に云っなけれて来です事にしたいじ。</p>
<p>場合をはいかに行くがすまましますでませから、いやしくもけっして考えて病気はいっそ広いなけれ事ん。</p>
</dd>
<input id="check2" type="checkbox">
<dt><label for="check2">ヘッダー</label></dt>
<dd>
<p>私は先刻すでにこの著作方について事の日を使うでた。はたして今朝を説明性もほとんどどんな批評でないかもが取りつかれからいるましには発会やりだでて、全くには死んうましでです。</p>
</dd>
</dl>
input[type="checkbox"]{
display: none;
}
label::after {
content: "▽";
transition: all .3s;
}
dd {
max-height: 0;
overflow: hidden;
transition: all .3s;
}
input[type="checkbox"]:checked + dt + dd {
max-height: 1000px;
}
input[type="checkbox"]:checked + dt label::after {
transform: rotate(180deg);
}
クリックをチェックボックスで判定する
ラベル要素でクリック領域を確保して、チェックボックス自体は(デザイン的に邪魔なので)非表示にします。
チェックボックスは :checked疑似クラスで選択状態を判定できるので、これを利用するわけですが、cssで指定できるようなhtmlの書き方をしなければいけません。
具体的には、クリックで動く要素(今回の場合は、アイコンの▽と開閉コンテンツ)を、チェックボックスと同じ階層かそれ以下にして、隣接セレクタ(+)で指定できるようにします。
間に何か挟まると途端に動かないので、注意が必要です。
<dl>
<input id="check1" type="checkbox">
<dt><label for="check1">ヘッダー</label></dt>
<dd>
<p>私は先刻すでにこの著作方について事の日を使うでた。はたして今朝を説明性もほとんどどんな批評でないかもが取りつかれからいるましには発会やりだでて、全くには死んうましでです。</p>
<p>先がなりな事はよし当時で引続きだたなかっ。しきりに岩崎さんに招待作物始終お尋ねを乗ったろこの生徒私か鑑定へというご教育たなましだと、その場合は私か畸形考でもたて、大森さんのものに壁の彼らにほとんどご落第と致さてあなた人真似をお病気にしようにようやくご真似にあれないたば、そんなにひょろひょろ講演に云っなけれて来です事にしたいじ。</p>
<p>場合をはいかに行くがすまましますでませから、いやしくもけっして考えて病気はいっそ広いなけれ事ん。</p>
</dd>
</dl>
<dl>
<input id="check2" type="checkbox">
<dt><label for="check2">ヘッダー</label></dt>
<dd>
<p>私は先刻すでにこの著作方について事の日を使うでた。はたして今朝を説明性もほとんどどんな批評でないかもが取りつかれからいるましには発会やりだでて、全くには死んうましでです。</p>
</dd>
</dl>
input[type="checkbox"]:checked ~ dd {
//チェックされたときの表示
}
こんなかんじで囲えるときは、チルダ(~)を使えます。
チルダ(~)は、チェックボックスの後ろにある、同じ階層の指定要素にスタイルを適用させられます。
この場合、チェックボックスと dd の間にどんな要素が挟まっていても大丈夫ですが、チェックボックスの後ろの dd すべてにスタイルが適用されてしまうので、開閉要素が複数あるときは、ひとつずつ囲う必要があります。
実装に向いてるケース
Javascriptを使えないときはこれしかないですね。
もしくは、htmlをコンパクトに書けるとき。開閉する要素ひとつずつにチェックボックスが必要なので、いくつもアコーディオンが並ぶときは煩雑です。