【アコーディオンFAQ】jQueryを使わないで実装する、コピペOKのアコーディオン【JavaScript】

アコーディオンFAQを

Vanilla.js(純粋なJavaScript)で実装する機会があったので備忘録として記載

アコーディオンFAQをこれから実装する人の参考になれば幸いです。

なぜjQueyをなぜ使わないのか?

ポイントイメージ

jQueryはInternet Explorer時代に一世を風靡したJavaScriptフレームワークで

純粋なJavaScriptを書くよりもスピード感を持って実装できる画期的なフレームワーク

Internet Explorerがサ終(サポート終了)になった今、

使うメリットがなくなり

スマホで反応しなかったり不具合が多数報告。

いつjQueryもサ終になるかわからないので、jQueryで実装することが返ってデメリットに感じている。

そこで、今回はVanilla.js(純粋なJavaScript)で実装することに

アコーディオンFAQ

See the Pen Untitled by 松村祐弥 (@lveklkok-the-vuer) on CodePen.

以下、使用しているコードです。

HTML
<div class="faq-item">

  <div class="faq-question__wrap">
    <h3 class="faq-question__title">ダミーテキスト</h3>
    <span class="minus-icon"></span>
    <span class="plus-icon"></span>
  </div>

  <div class="faq-answer__wrap">
    <p class="faq-answer__title">ダミーテキスト</p>
    <p class="faq-answer__text">ダミーテキスト</p>
  </div>

</div><!-- faq-item end -->

<div class="faq-item">

  <div class="faq-question__wrap">
    <h3 class="faq-question__title">ダミーテキスト</h3>
    <span class="minus-icon"></span>
    <span class="plus-icon"></span>
  </div>

  <div class="faq-answer__wrap">
    <p class="faq-answer__title">ダミーテキスト</p>
    <p class="faq-answer__text">ダミーテキスト</p>
  </div>

</div><!-- faq-item end -->

<div class="faq-item">

  <div class="faq-question__wrap">
    <h3 class="faq-question__title">ダミーテキスト</h3>
    <span class="minus-icon"></span>
    <span class="plus-icon"></span>
  </div>

  <div class="faq-answer__wrap">
    <p class="faq-answer__title">ダミーテキスト</p>
    <p class="faq-answer__text">ダミーテキスト</p>
  </div>

</div><!-- faq-item end -->

これが今回使用したhtmlです。

質問項目を増やしたり減らしたりしたい人は

.faq-itemを調整してください。

CSS
.faq-item {
  margin-top: 20px;
  max-width: 720px;
  margin-left: auto;
  margin-right: auto;

  border: 1px solid #0C89B7;
}

.faq-question__wrap {
  position: relative;
  background: #0C89B7;
  padding: 10px 5px 10px 55px;
  cursor: pointer;

}

.minus-icon {
  position: absolute;
  content: "";
  width: 20px;
  height: 3px;
  background: #fff;
  right: 20px;
  top: 50%;
  transform: translateY(-50%);
}
.plus-icon {
  position: absolute;
  content: "";
  width: 20px;
  height: 3px;
  background: #fff;
  right: 20px;
  top: 50%;
  transform: translateY(-50%) rotate(90deg);
  transition: ease all 0.5s; /* 擬似要素のトランジションを追加 */
}

.plus-icon.active {
  transform: translateY(-50%);
}

.faq-question__title {
  font-weight: bold;
  color: #FFF;
  font-size: 20px;
  position: relative;
}
.faq-question__title::before {
    position: absolute;
    color: #fff;
    content: "Q";
    top: 50%;
    left: -35px;
    transform: translateY(-50%);
  }

.faq-answer__wrap {
  background: #fff;
  padding: 10px 5px 10px 55px;
  transition: ease all 0.5s; /* 擬似要素のトランジションを追加 */
  height: 0;
  overflow: hidden;
  padding-top: 0;
  padding-bottom: 0;
}

.faq-answer__wrap.active {
  height: auto;
  padding: 10px 5px 10px 55px;
}
.faq-answer__wrap {
  background: #fff;
  opacity: 0;
  padding: 10px 5px 10px 55px;
  padding-top: 0;
  padding-bottom: 0;
  overflow: hidden;
  transition: ease all 0.7s; /* 擬似要素のトランジションを追加 */
}

.faq-answer__wrap.active {
  opacity: 1;
  padding: 10px 5px 10px 55px;
}

.faq-answer__title {
  position: relative;
  font-weight: bold;
  color: #0C89B7;
  font-size: 20px;
  color
}

.faq-answer__title::before {
    position: absolute;
    color: #0C89B7;
    content: "A";
    top: 50%;
    left: -35px;
    transform: translateY(-50%);
  }

.faq-answer__text {
  margin-top: 5px;
}

CSSです。

適宜変更してください。

JavaScript
document.addEventListener('DOMContentLoaded', function () {
  const faqItems = document.querySelectorAll('.faq-item');

  faqItems.forEach(item => {
    const questionWrap = item.querySelector('.faq-question__wrap');
    const answerWrap = item.querySelector('.faq-answer__wrap');
    const plusIcon = item.querySelector('.plus-icon');

    questionWrap.addEventListener('click', () => {
      // 回答表示の切り替え
      answerWrap.classList.toggle('active');

      // アイコンの切り替え
      plusIcon.classList.toggle('active');
    });
  });
});

これが今回使用したJavaScriptです。

JavaScriptの解説

HTMLの構造

このJavaScriptコードは、以下のようなHTML構造を持つアコーディオンを想定

HTML
<div class="faq-item">
  <div class="faq-question__wrap">
    <h3 class="faq-question__title">質問のタイトル</h3>
    <span class="minus-icon"></span>
    <span class="plus-icon"></span>
  </div>
  <div class="faq-answer__wrap">
    <p class="faq-answer__title">回答のタイトル</p>
    <p class="faq-answer__text">回答の内容</p>
  </div>
</div>
  • .faq-item が1つの質問と回答のセットになっています。
  • .faq-question__wrap が質問部分を、.faq-answer__wrap が回答部分を囲っています。
  • .plus-icon は質問が閉じているときに表示するアイコン、.minus-icon は質問が開いているときに表示するアイコンです。

JavaScriptの解説

  1. DOMContentLoadedイベント:document.addEventListener('DOMContentLoaded', function () { // ... });
    • DOMContentLoaded イベントは、HTMLドキュメントが完全に読み込まれて解析された後に発生します。このイベント内で処理を行うことで、HTML要素が確実に存在する状態でJavaScriptを実行できます。
  2. FAQアイテムの取得:const faqItems = document.querySelectorAll('.faq-item');
    • querySelectorAll(‘.faq-item’) を使用して、HTMLドキュメント内にある .faq-item クラスを持つすべての要素を取得し、faqItems に格納します。
  3. 各FAQアイテムへのイベントリスナー設定:faqItems.forEach(item => { // ... });
    • faqItems をループ処理し、各FAQアイテム (item) に対して以下の処理を行います。
  4. 要素の取得:const questionWrap = item.querySelector('.faq-question__wrap'); const answerWrap = item.querySelector('.faq-answer__wrap'); const plusIcon = item.querySelector('.plus-icon');
    • 現在のFAQアイテム (item) 内から、質問部分(.faq-question__wrap)、回答部分(.faq-answer__wrap)、プラスアイコン(.plus-icon)を取得し、それぞれ変数に格納します。
  5. クリックイベントリスナーの設定:questionWrap.addEventListener('click', () => { // ... });
    • 質問部分(questionWrap)にクリックイベントリスナーを設定します。クリックされると以下の処理が実行されます。
  6. 回答表示の切り替え:answerWrap.classList.toggle('active');
    • 回答部分(answerWrap)の active クラスを切り替えます。これにより、CSSで定義された active クラスのスタイルが適用・解除され、表示・非表示が切り替わります。
  7. アイコンの切り替え:plusIcon.classList.toggle('active');
    • プラスアイコン(plusIcon)の active クラスを切り替えます。これにより、CSSで定義された active クラスのスタイルが適用・解除され、アイコンの表示が切り替わります。

ポイント

  • active クラスのスタイルはCSSで定義します。
  • アコーディオンの開閉状態を初期表示と異なる状態にする場合は、JavaScriptで active クラスの追加・削除を調整します。