アコーディオンパネルの実装3種(JavaScript、jQuery、CSSオンリー)

なにかと実装する機会の多いアコーディオンパネル。
最近ではCSSだけでも実装できると聞いて試してみました。
ネイティブのJavaScript、jQueryでの実装方法も備忘録として残します。

目次

JavaScript アコーディオンパネル

ネイティブのJavaScriptで動かすアコーディオンパネルです。
下記の流れで動作する形となっています。

  • 開閉ボタン<div class=”accordion__head”>をクリック。
  • 親要素<li class=”accordion__item”>にactiveというclassが付与される。
  • アコーディオンの閉じている部分<div class=”accordion__body”>に当たるスタイルが変わる。(heightが0からautoに、paddingが上下0pxから上下10pxに)

動作サンプル

See the Pen Untitled by Yoshihiro Hotta (@yoshihiro-hotta) on CodePen.

HTML

<ul class="accordion__list">
  <li class="accordion__item">
    <div class="accordion__head">
      <p>アコーディオン1の開閉ボタン</p>
    </div>
    <div class="accordion__body">
      <p>アコーディオン1の中身</p>
    </div>
  </li>
  <li class="accordion__item">
    <div class="accordion__head">
      <p>アコーディオン2の開閉ボタン</p>
    </div>
    <div class="accordion__body">
      <p>アコーディオン2の中身</p>
    </div>
  </li>
  <li class="accordion__item">
    <div class="accordion__head">
      <p>アコーディオン3の開閉ボタン</p>
    </div>
    <div class="accordion__body">
      <p>アコーディオン3の中身</p>
    </div>
  </li>
</ul>

CSS

/*
  アコーディオンそのものの横幅と余白を指定。
  開閉動作に影響なし。
*/
.accordion__list {
  width: 500px;
  padding: 10px;
}

/*
  アコーディオンの2つ目以降、上に余白を取る。
  開閉動作に影響なし。
*/
.accordion__item:nth-child(n+2) {
  margin-top: 10px;
}

/*
  アコーディオンの開閉ボタンの装飾。
  position: relative;が無いと、
  「+」「-」の位置がずれるので注意。
*/
.accordion__head {
  position: relative;
  width: 100%;
  padding: 10px;
  color: #fff;
  background: #019ac6;
  cursor: pointer;
}

/*
  アコーディオンの開閉ボタンの右側に「+」を表示。
*/
.accordion__head::after {
  content: "+";
  position: absolute;
  top: 8px;
  right: 10px;
}

/*
  アコーディオンが開いた際に、
  開閉ボタンの右側に「+」を「-」に変更する。
*/
.accordion__item.active .accordion__head::after {
  content: "-";
  right: 13px;
}

/*
  アコーディオンの閉じている部分の装飾。
  height: 0; overflow: hidden;にすることで、
  通常時は表示されないようにする。
*/
.accordion__body {
  overflow: hidden;
  width: 100%;
  height: 0;
  padding: 0 10px;
  background: #e8e8e8;
  transition: all .25s ease;
}

/*
  アコーディオンが開いた際に、
  heightを0からautoに、paddingの上下を0から10に。
*/
.accordion__item.active .accordion__body {
  height: auto;
  padding: 10px;
}

JavaScript

// 厳格モードで実行
"use strict";

// アコーディオンパーツ全体と、開閉ボタンとなる部分のDOMを変数に格納。
const accordionItem = document.getElementsByClassName("accordion__item");
const accordionBtn = document.getElementsByClassName("accordion__head");

// 開閉ボタンがクリックされたときの処理。
// 開閉ボタンの数だけ処理できるようfor文を回す。
// accordionBtn[i](i部分には何番目のアコーディオンボタンか、の数字が入る)がクリックされた際、
// accordionItem[i](アコーディオンボタンと同じ数字のアコーディオンパーツ)に
// activeというcssのclassを付与する。
for(let i = 0; i < accordionBtn.length; i++) {
    accordionBtn[i].addEventListener("click", function() {
        accordionItem[i].classList.toggle("active");
    });
}

jQuery アコーディオンパネル

jQueryで動かすアコーディオンパネルです。

やっていること自体はJavaScriptと同じですが、こちらではfor文を回すのではなく開閉ボタンの親要素のDOMをparentメソッドで取得して、activeを付与する形にしています。

動作サンプル

See the Pen Untitled by Yoshihiro Hotta (@yoshihiro-hotta) on CodePen.

HTML

<ul class="accordion__list">
  <li class="accordion__item">
    <div class="accordion__head">
      <p>アコーディオン1の開閉ボタン</p>
    </div>
    <div class="accordion__body">
      <p>アコーディオン1の中身</p>
    </div>
  </li>
  <li class="accordion__item">
    <div class="accordion__head">
      <p>アコーディオン2の開閉ボタン</p>
    </div>
    <div class="accordion__body">
      <p>アコーディオン2の中身</p>
    </div>
  </li>
  <li class="accordion__item">
    <div class="accordion__head">
      <p>アコーディオン3の開閉ボタン</p>
    </div>
    <div class="accordion__body">
      <p>アコーディオン3の中身</p>
    </div>
  </li>
</ul>

CSS

/*
  アコーディオンそのものの横幅と余白を指定。
  開閉動作に影響なし。
*/
.accordion__list {
  width: 500px;
  padding: 10px;
}

/*
  アコーディオンの2つ目以降、上に余白を取る。
  開閉動作に影響なし。
*/
.accordion__item:nth-child(n+2) {
  margin-top: 10px;
}

/*
  アコーディオンの開閉ボタンの装飾。
  position: relative;が無いと、
  「+」「-」の位置がずれるので注意。
*/
.accordion__head {
  position: relative;
  width: 100%;
  padding: 10px;
  color: #fff;
  background: #F5675B;
  cursor: pointer;
}

/*
  アコーディオンの開閉ボタンの右側に「+」を表示。
*/
.accordion__head::after {
  content: "+";
  position: absolute;
  top: 8px;
  right: 10px;
}

/*
  アコーディオンが開いた際に、
  開閉ボタンの右側に「+」を「-」に変更する。
*/
.accordion__item.active .accordion__head::after {
  content: "-";
  right: 13px;
}

/*
  アコーディオンの閉じている部分の装飾。
  height: 0; overflow: hidden;にすることで、
  通常時は表示されないようにする。
*/
.accordion__body {
  overflow: hidden;
  width: 100%;
  height: 0;
  padding: 0 10px;
  background: #e8e8e8;
  transition: all .25s ease;
}

/*
  アコーディオンが開いた際に、
  heightを0からautoに、paddingの上下を0から10に。
*/
.accordion__item.active .accordion__body {
  height: auto;
  padding: 10px;
}

jQuery

// ページの全てのDOM要素が読み込まれて、初期化されてから実行。
$(document).ready(function() {
  // アコーディオン開閉ボタンのDOMを変数に格納。
  const accordionBtn = $(".accordion__head");
  // アコーディオン開閉ボタンがクリックされると、
  // 開閉ボタンの親要素(accordion__item)に、
  // activeというcssのclassが付与される。
  accordionBtn.on("click", function() {
      $(this).parent().toggleClass("active");
  });
});

CSSオンリー アコーディオンパネル

HTMLとCSSのみで動かすアコーディオンパネルです。

JavaScript、jQueryを使ったアコーディオンパネルと大きく違う点として、開閉ボタン部分がinputタグ(checkbox)とlabelタグになっています。

JavaScriptを一切使わないため、これまでのようにDOMに対してclassを付与することで、当たるスタイルを変える事はできません。
そのため、CSSの隣接兄弟結合子を使って、開閉ボタンと隣り合っているアコーディオンの閉じている部分に当たるスタイルを、checkboxにチェックが入っているかどうかで切り替える仕組みです。

MDN Web Docs
次兄弟結合子 - CSS: カスケーディングスタイルシート | MDN 次兄弟結合子 (next-sibling combinator, +) は 2 つのセレクターを接続し、 2 つ目の要素が 1 つ目の要素の 直後 にあって、両者が同じ親要素の子同士である場合に一致し...

流れとしては下記のような形となります。

  • 開閉ボタンのラベル<label class=”accordion__head” for=”accordion-1″>をクリック。
  • 連動するチェックボックス<input type=”checkbox” class=”accordion__input” id=”accordion-1″>が、チェックが入った状態になる。
  • アコーディオンの閉じている部分<div class=”accordion__body”>に当たるスタイルが変わる。(heightが0からautoに、paddingが上下0pxから上下10pxに)

動作サンプル

See the Pen Untitled by Yoshihiro Hotta (@yoshihiro-hotta) on CodePen.

HTML

<ul class="accordion__list">
  <li class="accordion__item">
    <!--
      type="checkbox"、id="accordion-1"のinputタグと
      for="accordion-1"のlabelタグを用意する。
      checkboxにチェックが付いたことを検知して、
      アコーディオンの隠れている部分の高さを変える。
    -->
    <input type="checkbox" class="accordion__input" id="accordion-1">
    <label class="accordion__head" for="accordion-1">アコーディオン1の開閉ボタン</label>
    <div class="accordion__body">
      <p>アコーディオン1の中身</p>
    </div>
  </li>
  <li class="accordion__item">
    <!--  -->
    <input type="checkbox" class="accordion__input" id="accordion-2">
    <label class="accordion__head" for="accordion-2">アコーディオン2の開閉ボタン</label>
    <div class="accordion__body">
      <p>アコーディオン2の中身</p>
    </div>
  </li>
  <li class="accordion__item">
    <!--  -->
    <input type="checkbox" class="accordion__input" id="accordion-3">
    <label class="accordion__head" for="accordion-3">アコーディオン3の開閉ボタン</label>
    <div class="accordion__body">
      <p>アコーディオン3の中身</p>
    </div>
  </li>
</ul>

CSS

/*
  アコーディオンそのものの横幅と余白を指定。
  開閉動作に影響なし。
*/
.accordion__list {
  width: 500px;
  padding: 10px;
}

/*
  アコーディオンの開閉をつかさどる、
  type="checkbox" を指定しているinputタグ。
  inputタグそのものは非表示にする。
*/
.accordion__input {
  display: none;
}

/*
  アコーディオンの2つ目以降、上に余白を取る。
  開閉動作に影響なし。
*/
.accordion__item:nth-child(n+2) {
  margin-top: 10px;
}

/*
  アコーディオンの開閉ボタンの装飾。
  position: relative;が無いと、
  「+」「-」の位置がずれるので注意。
*/
.accordion__head {
  position: relative;
  display: block;
  width: 100%;
  padding: 10px;
  color: #fff;
  background: #12DE40;
  cursor: pointer;
}

/*
  アコーディオンの開閉ボタンの右側に「+」を表示。
*/
.accordion__head::after {
  content: "+";
  position: absolute;
  top: 8px;
  right: 10px;
}

/*
  アコーディオンの閉じている部分の装飾。
  height: 0; overflow: hidden;にすることで、
  通常時は表示されないようにする。
*/
.accordion__body {
  overflow: hidden;
  width: 100%;
  height: 0;
  padding: 0 10px;
  background: #e8e8e8;
  transition: all .25s ease;
}

/*
  アコーディオンの開閉ボタン(labelタグ、class="accordion__head")をクリックすると、
  対となっているinputタグ(class="accordion__input")がチェック状態(checked)になる。

  チェック状態のinputタグの隣の隣にある、
  アコーディオンの閉じている部分(class="accordion__body")の
  heightとpaddingを変更し、アコーディオンを開いた状態にする。
*/
.accordion__input:checked + .accordion__head + .accordion__body {
  height: auto;
  padding: 10px;
}

/*
  チェック状態のinputタグの隣にある、開閉ボタンの「+」を「-」に変更する。
*/
.accordion__input:checked + .accordion__head::after
  {
  content: "-";
  right: 13px;
}

最後に

このぐらいシンプルなアコーディオンパネルだと、CSSオンリーでも遜色のない動きを実現できることがわかりました。

リッチな動きが不要なときは、ドンドン使っていこうと思います!

目次