この記事では、CSS GridとJavaScriptを使って、スムーズなアコーディオンを実装する方法を解説します。
完成イメージ
- 初期状態では中身が非表示
- ボタンをクリックするとスムーズに展開
- +/− アイコンが切り替わる
- 閉じたときは元の位置にスクロール
See the Pen Untitled by watanabe (@web-sourcecode) on CodePen.
コードの全体
HTML
<div class="c-accordion js-accordion"> <div class="c-accordion__content"> <div class="c-accordion__content__inner"> <p>dammy text</p> <p>dammy text</p> <p>dammy text</p> </div> </div> <button class="c-accordion__trigger js-accordion-trigger" type="button"> button<i class="c-accordion__trigger__icon"></i> </button></div>HTMLSCSS
.c-accordion { &__content { display: grid; grid-template-rows: 0fr; // 高さゼロで非表示 opacity: 0; transition: .36s grid-template-rows ease; &__inner { overflow: hidden; } .is-opened & { grid-template-rows: 1fr; // 高さを自動に opacity: 1; } } &__trigger { position: relative; display: block; cursor: pointer; text-align: center; font-weight: 600; border-radius: 100vmax; color: black; border: 2px solid black; background-color: #fff; width: 300px; margin: 20px auto 0; font-size: 16px; padding: 1em 0; &__icon { position: absolute; top: 0; right: 12px; width: 44px; height: 100%; &::before, &::after { content: ''; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background-color: black; } &::before { width: 18px; height: 2px; // 横線 } &::after { width: 2px; height: 18px; // 縦線 } .is-opened &::after { opacity: 0; // 縦線を消して−にする } } }}SCSSJavaScript
const items = document.querySelectorAll('.js-accordion');items.forEach(item => { const trigger = item.querySelector('.js-accordion-trigger'); if (trigger) { trigger.addEventListener('click', function (e) { e.stopPropagation(); const isActive = item.classList.contains('is-opened'); item.classList.toggle('is-opened'); if (isActive) { item.scrollIntoView({ behavior: 'instant', block: 'start' }); } }); }});JavaScriptポイント
grid-template-rowsを使った高さアニメーションmax-heightより滑らかで、中身の高さが可変でも対応可能。- アイコンは
::beforeと::afterでCSSだけで描画。 - 縦線を消すだけで+→−に切り替え。
.is-openedクラスの付け外しでCSSの状態を切り替え- 閉じるときは
scrollIntoViewで元の位置までスクロール
応用例
1つだけ開くアコーディオンにする場合は以下を追加します。
items.forEach(item => { const trigger = item.querySelector('.js-accordion-trigger'); if (trigger) { trigger.addEventListener('click', e => { e.stopPropagation(); items.forEach(i => i.classList.remove('is-opened')); // 全部閉じる item.classList.add('is-opened'); // クリックしたものだけ開く }); }});JavaScriptまとめ
アコーディオンはFAQやメニューに限らず、コンテンツの折りたたみにも応用できます。
CSS Gridを使えば、よりモダンで柔らかな動きを簡単に実現できます。