PATAPATA WORKS

アコーディオンメニューのつくりかた

アコーディオンメニューとは、項目を押したら隠れていた文章などがパカパカと開閉するように開いたり閉じたりするメニューになります。
そのパカパカ具合がアコーディオンみたいだからアコーディオンメニューと呼ばれています。

実際利用頻度は高く、そのままだと長いコンテンツを短くまとめる時などに効果を発揮するメニューになります。

こちらのアコーディオンメニューの作り方をざっくりとご紹介いたします。

セマンティックなアコーディオンメニュー

まずは、セマンティック(HTMLの構造的に意味を持たせる)タイプのアコーディオンメニューの書き方になります。
この記載方法ですと、ここがアコーディオンメニューだよと内容を正しく記載する事が可能になります。

HTML

<details>
  <summary>
    アコーディオン
  </summary>
  <div class="accordion__inner">
    <h1>アコーディオンの内容</h1>
    <p>この部分がアコーディオンします</p>
    <p>この部分がアコーディオンします</p>
    <p>この部分がアコーディオンします</p>
  </div>
</details>

上記のHTMLがアコーディオンメニューになります。

<details>の中に挟まれているものは基本的に非表示になりますが、その中にある<summary>だけは開閉ボタンとして表示されます。

このsummaryの部分を押せば、それ以外のもの(上記ではaccordion__inner部分)が表示と非表示を行うようになります。

アコーディオンが開いた時は,<details>openが付きますので

details[open] {
  background:#f00;
}

上記のように開いた時専用のCSSを追加することも可能です。

ですが、この方法は1つだけ問題があります。このタグを使用するとアニメーションさせるのがものすごく難しいです。
閉まっている時は中の要素は完全に消えた扱いになるので即時表示、即時消去を解消してアニメーションさせるのはかなりの手間になると思います。

また、中にあるコンテンツを<details>の外に出してしまえば問題なくアニメーションさせることは出来ますが、それだとセマンティックを適用させてない事になるので本末転倒です。

アニメーションをしない、セマンティックなコードを書く必要がある場合はこちらですが、アコーディオンの開閉をアニメーションをさせたい場合は<details>を諦めて、普通のHTMLを書いてそれをアコーディオンさせるようにしたほうが早いですね。

アニメーションするアコーディオンメニュー

やっぱりアコーディオンメニューは開閉アニメーションがあってなんぼですよ。という場合はやはりいつもの方法で作成する事になります。

HTML

<section class="accordion">
  <button type="button" class="accordion__opener">
    アコーディオン 2-1
  </button>
  <div class="accordion__content">
    <div class="accordion__slide">
      <div class="accordion__inner">
        <h1>アコーディオンの内容 1</h1>
        <p>この部分がアコーディオンします</p>
      </div>
    </div>
  </div>
</section>

<section class="accordion">
  <button type="button" class="accordion__opener">
    アコーディオン 2-2
  </button>
  <div class="accordion__content">
    <div class="accordion__slide">
      <div class="accordion__inner">
        <h1>アコーディオンの内容 2</h1>
        <p>この部分がアコーディオンします</p>
        <p>この部分がアコーディオンします</p>
      </div>
    </div>
  </div>
</section>

CSS

.accordion__content {
  display: grid;
  background: #ccc;
  grid-template-rows: 0fr;
  min-height:0;
  transition: grid-template-rows 0.5s ease-out;
}
.accordion__content.is-open {
  grid-template-rows: 1fr;
}
.accordion__slide {
  overflow: hidden; 
}
.accordion__inner {
  padding:1rem;
}

Javascript

window.onload = function () {
  const openers = document.querySelectorAll(".accordion__opener");
  openers.forEach(function (opener) {
    opener.addEventListener("click", function () {
      const content = this.nextElementSibling;
      if (content) {
        content.classList.toggle("is-open");
      }
    });
  });
};

ボタンを押すと特定のclassがONOFFして後はCSSで動かす基本パターンによるアコーディオンメニューです。

アコーディオンボタンは複数個置く事が多いので、ID等を使わず、置いてあるclassだけで動作するようになっています。ボタンを押したらイベントが発火します。this.nextElementSiblingは直後の兄弟要素の指定を行います。

この場合ですと<button>の後に来る<div>を指定します。直後にあるもの限定ですが、このようにしておくことで、複数のアコーディオンメニューを個別に操作することが出来るようになります。

開閉にはgrid-template-rowsを使用します。ONOFFさせることにより、これを0frと1frで切り替えます。これで中のコンテンツの高さを取得せずとも普通に開閉させることが出来るようになります。

accordion__slide と accordion__inner を分けている理由

accordion__contentaccordion__slideaccordion__innerと3層構造になった原因としましては、accordion__contentとその子要素accordion__slideにpaddingが入っていると、0frにしてもpadding分の隙間が空く為、対処としての3層構造になっています。

ですが、孫要素を作ってしまうと、accordion__contentoverflow:hiddenを入れても、孫要素のaccordion__innerの要素の高さを拾ってしまいアコーディオンが閉じないのです。
そのため、子要素であるaccordion__slideoverflow:hiddenを入れることが重要になります。

結果的にはaccordion__slideの中にaccordion__innerを作り、それにpaddingを設定し、accordion__slideの中にoverflow:hiddenを入れることで隙間なくアコーディオンを閉じることが出来るようになります。

このあたりの挙動は面倒なので気をつけないと何処が間違ったのかわからなくなったりします。

このような感じでざっくりとアコーディオンメニューを作ってみましたが、色々と覚えておいて損はないことも色々覚えられるので覚え直してみるのも良いですね。

もしも、ホームページ制作でお困りでしたらパタパタワークスがあなたの力になれるかもしれません。ちょっとしたことでもお気軽にお問い合わせください。それではまた!