アコーディオンUI(開閉メニュー)の作り方

このページの目的

質問と回答のように、最初はタイトルだけを見せておき、ユーザーがクリックすると詳細な内容が開く「アコーディオンUI」の実装方法を学びます。
このUIを使うことで、多くの情報をスッキリと見せることができ、ページが縦に長くなりすぎるのを防ぎます。

smart_display動作サンプル

実際に動作するサンプルです。質問部分をクリックして、回答が開閉することを確認してみてください。

このサンプルを実装するためのHTML, CSS, JavaScriptのコードは以下の通りです。


code実装コード解説

1. HTMLの構造

ボタン(.accordion-trigger)と、開閉するパネル(.accordion-panel)をセットで用意します。

<div class="accordion-container">
  <h4 class="accordion-header">
    <button class="accordion-trigger" aria-expanded="false" aria-controls="content-1">
      <span class="accordion-title">質問タイトル</span>
      <span class="accordion-icon"></span>
    </button>
  </h4>
  <div id="content-1" class="accordion-panel" hidden>
    <div class="accordion-content">
        <p>回答のコンテンツ</p>
    </div>
  </div>
</div>

2. CSSのスタイル

パネルの表示・非表示と、開閉アニメーション、アイコンの回転などを設定します。

.accordion-trigger.is-active .accordion-icon {
  transform: rotate(45deg); /* is-activeでアイコンを回転 */
}

.accordion-panel {
  visibility: hidden;
  max-height: 0;
  opacity: 0;
  transition: max-height 0.3s ease-out, visibility 0s 0.3s, opacity 0.3s 0s;
}
.accordion-panel.is-active {
  visibility: visible;
  max-height: 500px; /* 十分な高さを確保 */
  opacity: 1;
  transition: max-height 0.4s ease-in, visibility 0s 0s, opacity 0.3s 0.1s;
}
.accordion-content {
  overflow: hidden;
  padding: 0 1.2em 1em;
}

3. JavaScriptの処理

トリガーがクリックされたら、対応する要素の属性やクラスを切り替えます。

(function() {
    'use strict';

    function initAccordions() {
        const accordionContainers = document.querySelectorAll('.accordion-container');

        accordionContainers.forEach(container => {
            const trigger = container.querySelector('.accordion-trigger');
            const panel = container.querySelector('.accordion-panel');

            if (trigger && panel) {
                trigger.addEventListener('click', function() {
                    const isExpanded = this.getAttribute('aria-expanded') === 'true';

                    this.setAttribute('aria-expanded', !isExpanded);
                    panel.hidden = isExpanded;
                    
                    this.classList.toggle('is-active');
                    panel.classList.toggle('is-active');
                });
            }
        });
    }

    document.addEventListener('DOMContentLoaded', initAccordions);

})();