【JavaScript・jQuery】アコーディオンメニュー。アコーディオン内に下層のアコーディオンも実装できるよ

こんにちは、yukipanです。
メニューやコンテンツがたくさんあるときにアコーディオンでギュッと凝縮できるの、便利ですよね。見たいところだけ開いてみてくださーい、みたいな。

CSSのみで実装できるけど、CSSはinputやらlabelやら使うし、やっぱりJS使ったほうが実装ラク。
コピペで使えるアコーディオンの、JavaScriptバージョンとjQueryバージョンをメモしておきます。

サンプルサイト

アコーディオンに必要なパーツ

とりあえずこれだけ記述すればアコーディオン動くよ〜っていうパーツだけ。

HTML

<p class="acco_menu">アコーディオンメニュー</p>
<div class="acco_contents">
<!-- ここにアコーディオンの中身が入ります -->
</div>

JavaScript

<script>
const acco = document.querySelectorAll(".acco_menu");
function accordion() {
  const content = this.nextElementSibling;
  content.classList.toggle("is-acco");
};
for (let i = 0; i < acco.length; i++) {
  acco[i].addEventListener("click", accordion);
};
</script>

nextElementSiblingで、「.acco_menu」の次の要素に「is-acco」っていうclassを付与する。
.acco_menuの場所をclickしたらclass付けるよ〜っていうのをループ。

CSS

.acco_contents {
  display: none;
}

.acco_contents.is-acco {
  display: block;
}

「.acco_menu」の次の要素に「.acco_contents」っていうclassを記述しておいて、「display: none;」で非表示にしておく。
「is-acco」が付いたら「display: block;」で表示。

jQueryの場合

<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<script type="text/javascript">
  $(".acco_menu").on("click", function () {
    $(this).next().slideToggle();
  });
</script>
.acco_contents {
  display: none;
}

jQueryだったら、「is-acco」が付いたとき〜などの記述は必要ない。
CSSはdisplay: none;のみ記述しておいて、クリックしたら直に「display: block;」が付与される。

アコーディオンでよく見るボタンみたいな「+」とか付けたい

上記記述でアコーディオンが動くのは動いた。
けど、アコーディオンでよく見る「+」「ー」とか、「^」のマークも付けたい!って思いますよね?
クリックしたら「+」が「ー」になるようにするためには、JSとCSSに追記します。

JavaScript

const acco = document.querySelectorAll(".acco_menu");
  function accordion() {
    const content = this.nextElementSibling;
    this.classList.toggle("is-active"); //ボタン用
    content.classList.toggle("is-acco");
  };
  for (let i = 0; i < acco.length; i++) {
    acco[i].addEventListener("click", accordion);
  };

はい、とってもカンタン。「this.classList.toggle(“is-active”);」を追加するだけ。
クリックしたら「.acco_menu」に「.is-active」を付けてね〜っていう。

jQueryの場合

<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<script type="text/javascript">
  $(".js-acco").on("click", function () {
    $(this).next().slideToggle();
    $(this).toggleClass("is-active"); //ボタン用
  });
</script>

jQueryだと、「$(this).toggleClass(“is-active”);」で「is-active」が付与されます。

CSS「+」バージョン

.acco_menu{
  position: relative;
  cursor: pointer;
}
.acco_menu::before,
.acco_menu::after {
  content: '';
  width: 20px;
  height: 3px;
  background: #000;
  position: absolute;
  top: 50%;
  right: 5%;
  transform: translateY(-50%);
}
.acco_menu::after {
  transform: translateY(-50%) rotate(90deg);
  transition: .5s;
}

/* .is-activeが付いたら「ー」にする */
.is-active.acco_menu::after { 
  transform: translateY(-50%) rotate(0);
}

beforeとafterで「+」を作って、「is-active」が付いたら「ー」になるよう記述。

CSS「∨」バージョン

.acco_menu{
  position: relative;
 cursor: pointer;
}
.acco_menu::after {
  border-top: 3px solid #000;
  border-right: 3px solid #000;
  content: '';
  position: absolute;
  top: calc(50% - 10px);
  right: 10%;
  width: 10px;
  height: 10px;
  transform: rotate(135deg);
  transition: .3s;
}

/* .is-activeが付いたら「∧」にする */
.is-active.acco_menu::after {
  transform: rotate(-45deg);
  top: calc(50% - 3px);
}

beforeとafterで「∨」を作って、「is-active」が付いたら「∧」になるよう記述。

アコーディオンの動きをなめらかにしたい

displayで操作すると、どうしてもパキッとした動きになってしまう。
そのため、CSSでdisplay:none;を使わずなめらかな動きに調整。

CSS

.acco_contents {
  line-height: 0;
  height: 0;
  overflow: hidden;
  opacity: 0;
  transition-duration: .4s;
  }

.is-acco.acco_contents {
  line-height: normal;
  height: auto;
  opacity: 1;
  padding: 10px 0;
}

height: 0; opacity: 0;などにして非表示の状態にしておいて、「is-acco」が付いたら、height: auto; opacity: 1;にして表示させる。transition-duration: .4s;で動きをなめらかに。

jQueryだとCSSで動きをなめらかにする必要がない

jQueryから離れたいけど離れられないのはこういうところなんだよね。
記述少なくても、なめらかにアコーディオンが動く〜。
少ない記述でカンタンに動くのがjQueryのいいところ。

どっちを使うかは個人の自由だけど、jQuery不要論を唱えている人も多い気がする。
できるだけCSSやJavaScriptで記述したいけど、使い所によっては使ってもいいんじゃないかなーって思う。

参考サイト

アコーディオンメニューはJavaScriptで作れる!【初心者もOK】

こちらのサイトを参考にさせていただきました。
JavaScript初心者にも優しく詳しく解説してあるので良かったら参考にしてみてください。

CSSのみで実装するアコーディオンも作ってみた。

Q&Aくらいのカンタンなアコーディオンで良ければ、inputとlabelを使ってCSSのみで実装が可能。
よかったらこちらもどうぞ。

CSSのみで実装。アコーディオンメニュー