【Javascript】アコーディオンメニューを最小コードで実装する

アコーディオンメニュー

当記事では、jQueryなどの外部ライブラリを使用せず、アコーディオンメニューを実装する方法についてデモ付きで記します。

素のJavascriptでアコーディオンの開閉アニメーションを実装しようとすると煩雑になりがちですが、コード量を抑える事を目指します。






⓪要件

当記事では少ないコードでアコーディオンメニューを実装する事を目指していますが、機能を疎かにするつもりはありません。
一般的なアコーディオンメニューに求められる下記の要件を満たす予定です。

  • 開閉アニメーションをつける
  • アクセス時の開閉状態を指定できるようにする
  • 開いている時と閉じている時で矢印の向きを変える
  • 複数置いても問題なく動くように



ここからは1ステップずつ詳しく解説していきますが、過程はいいからソースコードを提示してくれという方はこちらからソースコード全文までスキップ頂けます。





①HTMLでアコーディオンメニューのパーツを用意する

まずはHTMLでアコーディオンメニューのボタンとコンテンツを用意します。

<div class="accordion-btn">アコーディオンボタン</div>
<div class="accordion-content">
    <p>アコーディオンコンテンツ</p>
</div>





②Javascriptで開閉処理を実装する

複数アコーディオンメニューを置いても動くようにforEachを使用します。
また、開閉処理に必要なスタイルはわかりやすくすいようにCSSではなくJavascriptで設定します。
ボタンクリックでコンテンツの開閉を行うイベントの登録を6行目で行っています。

document.querySelectorAll('.accordion-btn').forEach(function(el){
    const next = el.nextElementSibling
    const nextH = next.scrollHeight + 'px'
    next.style.overflow = 'hidden'
    next.style.height = 0
    el.onclick = () => next.style.height = el.classList.toggle('open') ? nextH : 0)
})


上記のコードではtoggleメソッドでopenクラスを付け外ししているので、開閉の状態はCSSで定義したらいいのではと思った方もいらっしゃるかもしれません。
しかし、height:0からheight:autoへの変更に対してtransitionは効かないので(2023年10月時点)、CSSでは高さが可変のコンテンツの開閉アニメーション(後で実装)に対応できません。

CSSでもpaddingmax-heightを使ってそれっぽく見せることはできますが、綺麗に開閉アニメーションを実装しようと思うとJavascriptで行うのがベストかなと個人的に考えています。





ここまでのデモを一旦載せておきます。要件の1/4を満たしています。

  • 開閉アニメーションをつける
  • アクセス時の開閉状態を指定できるようにする
  • 開いている時と閉じている時で矢印の向きを変える
  • 複数置いても問題なく動くように
アコーディオンボタン

アコーディオンコンテンツ

アコーディオンボタン

アコーディオンコンテンツ

アコーディオンボタン

アコーディオンコンテンツ





③CSSで見た目を整える

わかりやすいようにCSSで見た目を整えておきましょう。
7行目のpaddingはコンテンツ自身ではなくコンテンツの中の要素に指定しないと見た目が崩れるので注意が必要です。

.accordion-btn {
    cursor: pointer;
    background: skyblue;
    padding: 10px;
}
.accordion-content p{
    padding: 10px;
}





④開閉アニメーションをつける

開閉アニメーションをつけるにはアコーディオンコンテンツにtransitionを指定するだけでOKです。

document.querySelectorAll('.accordion-btn').forEach(function(el){
    const next = el.nextElementSibling
    const nextH = next.scrollHeight + 'px'
    next.style.overflow = 'hidden'
    next.style.transition = '0.5s'      // 追加
    next.style.height = 0
    el.onclick = () => next.style.height = el.classList.toggle('open') ? nextH : 0
})


ここまでのデモを一旦載せておきます。要件の2/4を満たしています。

  • 開閉アニメーションをつける
  • アクセス時の開閉状態を指定できるようにする
  • 開いている時と閉じている時で矢印の向きを変える
  • 複数置いても問題なく動くように
アコーディオンボタン

アコーディオンコンテンツ

アコーディオンボタン

アコーディオンコンテンツ

アコーディオンボタン

アコーディオンコンテンツ






⑤アクセス時の開閉状態を指定できるようにする

特定のアコーディオンメニューのみ最初から開いた状態にしておきたい場合などがあると思います。
アコーディオンボタンにopenクラスがついていたら開いた状態になるようにしましょう。

document.querySelectorAll('.accordion-btn').forEach(function(el){
    const next = el.nextElementSibling
    const nextH = next.scrollHeight + 'px'
    next.style.overflow = 'hidden'
    next.style.transition = '0.5s'
    next.style.height = el.classList.contains("open") ? nextH : 0   //修正
    el.onclick = () => next.style.height = el.classList.toggle('open') ? nextH : 0
})


最初から開いておきたいアコーディオンのボタンにopenクラスを付けておくことで、コンテンツが開いた状態で表示されます。

<div class="accordion-btn open">アコーディオンボタン</div>  <!-- 修正 -->
<div class="accordion-content">
    <p>アコーディオンコンテンツ</p>
</div>
<div class="accordion-btn">アコーディオンボタン</div>
<div class="accordion-content">
    <p>アコーディオンコンテンツ</p>
</div>


ここまでのデモを一旦載せておきます。要件の3/4を満たしています。

  • 開閉アニメーションをつける
  • アクセス時の開閉状態を指定できるようにする
  • 開いている時と閉じている時で矢印の向きを変える
  • 複数置いても問題なく動くように
アコーディオンボタン

アコーディオンコンテンツ

アコーディオンボタン

アコーディオンコンテンツ

アコーディオンボタン

アコーディオンコンテンツ






⑥開いている時と閉じている時で矢印の向きを変える

一般的なアコーディオンメニューはわかりやすいようにボタンに矢印などついていることが多いです。
最後に開いている時と閉じている時でそれぞれ違う矢印を表示するようにしましょう。

CSSの::after疑似要素をしようして各矢印をアコーディオンボタンの一番右に表示します。

.accordion-btn::after{
    content: '▼';
    float: right;
}
.accordion-btn.open::after{
    content: '▲';
}



ここまでのデモを載せておきます。以下の要件をすべて満たしました。

  • 開閉アニメーションをつける
  • アクセス時の開閉状態を指定できるようにする
  • 開いている時と閉じている時で矢印の向きを変える
  • 複数置いても問題なく動くように
アコーディオンボタン

アコーディオンコンテンツ

アコーディオンボタン

アコーディオンコンテンツ

アコーディオンボタン

アコーディオンコンテンツ







ソースコード全文

最後にソースコードを全文載せておきます。

<style>
    .accordion-btn {
        cursor: pointer;
        background: skyblue;
        padding: 10px;
    }
    .accordion-content p{
        padding: 10px;
    }
    .accordion-btn::after{
        content: '▼';
        float: right;
    }
    .accordion-btn.open::after{
        content: '▲';
    }
</style>

<div class="accordion-btn">アコーディオンボタン</div>
<div class="accordion-content">
    <p>アコーディオンコンテンツ</p>
</div>

<script>
    document.querySelectorAll('.accordion-btn').forEach(function(el){
        const next = el.nextElementSibling
        const nextH = next.scrollHeight + 'px'
        next.style.overflow = 'hidden'
        next.style.transition = '0.5s'
        next.style.height = el.classList.contains("open") ? nextH : 0
        el.onclick = () => next.style.height = el.classList.toggle('open') ? nextH : 0
    })
</script>







以上、アコーディオンメニューを最小コードで実装する方法でした。

スポンサーリンク

You can subscribe by SNS

スポンサーリンク