アコーディオンメニューの実装(JavaScriptありorなし)

投稿日

アコーディオンメニューの実装(JavaScriptありorなし)
投稿者:ブンタ ブンタ

今回はWebサイトやLPでよく使われるアコーディオンメニューの実装方法を解説します。

HTMLとCSSだけでできるものと、JavaScriptが入ったものそれぞれの解説をします。

ぜひ参考にしてみてください。

アコーディオンメニューとは?

アコーディオンメニューは、クリックやタップで表示・非表示が切り替わるメニュー形式で、一般的にサイドバーやQ&Aコーナーなどで使われます。
このメニュー形式では、見出し部分をクリックすると、コンテンツが展開され、もう一度クリックすると折りたたまれます。

WebサイトやLPでは、ユーザーの視認性やユーザー体験の向上を目的として、導入されます。

HTMLによる実装

一見難しそうなアコーディオンメニューですが、HTMLとCSSだけでも簡単に実装できます。
まずはHTMLを記述します。

<details>
  <summary>accordion title</summary>
  <div class="contents">
    accordion contents
  </div>
</details>

detailsタグを使い、中にsummaryタグを入れます。タイトルはsummaryタグの中に記述します。(Q&AではQの部分です。)その下にはdivタグで内容を記述します。(Q&AだとAの部分です。)

次は、最低限のCSSを記述します。

details {
  summary {
   cursor: pointer;
   padding: 1.5em 2em;
 }
 .contents {
   padding: 2em;
 }
}

これで完成です。
これを複数並べ、文字色や背景色を変えると以下のようになります。

See the Pen accordion_html by Bunta (@bbunta) on CodePen.

 

JavaScriptなしの問題点

HTMLとCSSだけで実装する場合は非常に簡単ですが、以下のような問題点があります。

  • 動きが滑らかではない。(カクカクしている)
  • 中身をクリックしてもメニューが閉じない。
  • 他のアイテムを開いた時に、今開いているアイテムが閉じない。

少しリッチなサイトにしたい場合は、JavaScriptは必要になります。

JavaScriptありの場合

まずはHTMLを記述します。

<dl>
  <dt class="acHeader">accordion title</dt>
  <dd><div>accordion contents</div></dd>
</dl>

dl,dt,ddタグで実装しています。
全てdivタグでも作れますが、アクセシビリティを意識する場合はこちらをおすすめします。
また、クラス名は一箇所だけつけています。
後に記述するJavaScriptでもこの要素だけ取得するようにします。

続いてCSSです。

dl {
  dt {
    position: relative;
    padding: 1.5em 2em;
    cursor: pointer;
  dd {
     opacity: 0;
     visibility: hidden;
     max-height: 0;
     transition: 0.3s;  
    div {
      padding: 2em;
    }
  }
}

今回もできる限りシンプルにしています。
先ほどとの大きな違いは、ddを非表示にしている点です。
JavaScriptで表示、非表示を切り替えるためです。

では、JavaScriptです。

JavaScriptは少し長いので、分けて解説します。

  const headers = document.querySelectorAll(".acHeader");
  headers.forEach((header) => {
    header.addEventListener("click", () => toggleAccordion(header));

    const content = header.nextElementSibling;
    if (content) {
      content.addEventListener("click", () => toggleAccordion(header));
    }
  });

まずはacHeaderを取得します。複数ある場合を想定してquerySelectorAllを使っています。
それをforEachループで一つずつ回しています。

ここでのheaderはdtです。dtをクリックするとtoggleAccordion関数を実行するようにプログラミングしています。クリックすると開いて、もう一度クリックすると閉じるようにするためです。
次はcontentです。header.nextElementSiblingで、headerの次の要素を取得しています。今回のケースではddです。dtと同じく、クリックするとtoggleAccordion関数が実行するようにしています。コンテンツをクリックした場合もメニューを閉じるようにするためです。

続いてtoggleAccordion関数を設定します。

const item = header.parentElement;
const content = header.nextElementSibling;

if (item.classList.contains("active")) {
  item.classList.remove("active");
  content.style.maxHeight = "";
  content.style.opacity = "0";
  content.style.visibility = "hidden";
} else {
  item.classList.add("active");
  content.style.maxHeight = content.scrollHeight + "px";
  content.style.opacity = "1";
  content.style.visibility = "visible";
}

itemはheader.parentElementなので、今回のケースではdlです。contentは先ほどと同じddです。
その下は条件分岐です。もしdlにactiveクラスがついていれば、ddは非表示にするように設定しています。それ以外の場合、つまりdlにactiveクラスが付いていない場合は、ddを表示するようにしています。

これだけでもいいですが、今回はさらに、他のメニューをクリックした場合に、今開いているメニューを閉じるコードも付け加えておきます。
toggleAccordion関数の中に追記します。

headers.forEach((hdr) => {
  const itm = hdr.parentElement;
  const cnt = hdr.nextElementSibling;

  if (itm !== item && itm.classList.contains("active")) {
    itm.classList.remove("active");
    if (cnt) {
      cnt.style.maxHeight = "";
      cnt.style.opacity = "0";
      cnt.style.visibility = "hidden";
    }
  }
});

もし、クリックした要素以外にactiveクラスがついている場合は、activeクラスを外して、ddは非表示にするといったプログラミングをしています。

これらをまとめると以下のようになります。
おまけでcssで作った矢印もつけておきます。

See the Pen accordion_js by Bunta (@bbunta) on CodePen.

クリックするとアコーディオンメニューが開き、他のメニューをクリックすると、開いているメニューが閉じるのが確認できます。

まとめ

今回は、アコーディオンメニューの実装方法をHTMLとCSSのみのパターンと、そこにJavaScriptを加えたパターンを紹介しました。

JavaScriptは慣れないと難しい部分もありますが、少しずつ読み解いていけば、徐々に理解できるようになります。

今後も当サイトでは様々なコードやプログラミングの実装方法などを紹介していきますので、ぜひ楽しみにしていてください。

フロントエンドエンジニアやコーダー向けに、以前書いた以下の記事もおすすめです。こちらもぜひご覧ください。

font-sizeの単位はremにしよう!62.5%は非推奨!!