CSS アニメーションを駆使して軽量アコーディオンメニューの実装方法をご紹介。
jQueryに依存しないと軽量になります。今回は「detailsとsummaryタグを使う新方法(追加)」「labelとinputタグを使う旧方法」「JavaScript(jQuery不使用)なめらかアニメーションの使ってさらにリッチに実装する方法(レスポンシブ対応のため修正)」の3パターンをご紹介します。もちろん高さの可変に対応したコードも有りますので、目次を参考にしてください。
原理を理解したい方は順を追った解説をまず読み進めてみてください。
かみーゆ/フロントエンドエンジニア
アコーディオンメニューにCSSを主軸に実装する理由
言うまでもなく、CSSアニメーションはJSのDOM操作より軽いです。
一方デメリットとしてはCSSが複雑かつコードが長くなり初学者は混乱しやすいことです。
今回は「details&summaryを使う方法」「label&inputを使う方法」「JavaScript(jQuery不使用)も使って実装する方法」の3種類を紹介します。すべて一長一短あります。
種類 | メリット | デメリット |
---|---|---|
details&summary | WAI-ARIAなどのアクセシビリティへの配慮が不要 | IE未対応。safariのバグあり |
label&input | 手軽でJS不要。ブラウザをあまり考慮する必要がない(IEすら動く) | アクセシビリティに関して不適切 |
JS | WAI-ARIAなどの記述や値の動的変更が可能。ブラウザをあまり考慮する必要がない | 学習コストが上がる。少し挙動が重くなる可能性あり |
detailsとsummaryタグを使う方法(2021-01-05追記)
details
とsummary
タグ(詳細折りたたみ要素)を使えば、アニメーションの滑らかさにはかけますが、CSSすら使わずにアコーディオンを実装できます。
セマンティック(タグに意味を持たせる)コーディングという観点からもこちらのタグを使うのが良さそうです。が、問題もあります。
Safariはバグだらけで対応が大変。複雑なアニメーションは避けたほうがよさそう。
details
タグ内ではtransform
プロパティのanimation
やtransition
系が動かないことがある。animation
を実装してみたが最初の1回だけしか動かなかった。flex
が効かない
なので、こんなアイコンつけたかったけど諦めました。
<details>
<summary>メニュー1</summary>
<div>
<p>コンテンツがここに入ります</p>
</div>
</details>
<details>
<summary>メニュー2</summary>
<div>
<p>コンテンツがここに入ります</p>
</div>
</details>
<details>
<summary>メニュー3</summary>
<div>
<p>コンテンツがここに入ります</p>
</div>
</details>
details
タグ内のsummary
以降のコンテンツは開閉対象になってしまうので、アコーディオンしたいコンテンツごとにdetail
タグでラップする必要があります。
最初から開いておきたい場合はopen
属性を付与します。
<details open>
<summary>メニュー3</summary>
<p>コンテンツがここに入ります</p>
</details>
デフォルトのダサいアイコンを消したい時は、summaryのCSSを以下のようにセットしておきます。
summary {
list-style: none;
}
summary {::-webkit-details-marker {
display: none;
}
パターン1:コンテンツの高さが決まっている場合
実際にアニメーションを入れます。今回はheightを指定して入れてみました。
diplay:flex
も効かないので縦方向はline-height
で真ん中に持ってきてます。
* {
box-sizing: border-box;
}
details {
transition: 0.5s;
height: 51px;
margin-bottom: 1px;
border: 1px solid #333;
overflow: hidden;
}
details:last-child {
margin-bottom: 0;
}
details[open] {
height: 100px;
}
details summary {
height: 50px;
line-height: 50px;
align-items: center;
list-style: none;
padding: 0 15px;
background: #333;
color: #fff;
position: relative;
}
details summary::-webkit-details-marker {
display: none;
}
details div {
padding: 0 15px;
}
details div p {
height: 50px;
line-height: 50px;
}