【完全版】JavaScriptでFAQを設置する方法

よくある質問はCSSだけで作れるのかな?

こんな悩みを抱える人も多いのではないでしょうか?

現状はCSSだけでFAQのアコーディオンを作るのは可能なんですが、開けるように知らせるプラスマークや、他の動きを追加するなどの機能はCSSだけでは難しいです。

なので、ここではFAQのアコーディオンをCSSとJavaScriptを使って作っていきたいと思います!

最近のコーポレートサイトには結構な頻度でFAQのセクションが設置されているので、ぜひFAQの作り方をマスターしてドヤ顔で作ってみてください!

完成予想図はこのような見た目になります!!

FAQの設置の流れはこのような段取りで設置していきます!!

  • HTMLでFAQのパーツを作る
  • CSSで見た目を整える(FAQをクリック後のスタイルも作る)
  • JavaScriptでFAQの動きの部分を作る

こんな流れで作っていきます!!わかりづらいところは詳しく解説を入れていきますね!

アコーディオンを使ったFAQの作り方

ホームページにやってきたユーザーにアコーディオンと気づいてもらうには、これは開きますよ!という+ボタンがアコーディオンには必要です!

別に+ボタンではなくてもいいのですが、この+ボタンが無いとアコーディオンだとユーザーに気づいてもらえない可能性があります。

実は+ボタンはFAQのアコーディオンにとって重要なパーツになります。

アコーディオンを開く時に+ボタンが動くようにしているアコーディオンが多いですよね。

CSSだけだとアコーディオンを開く動きはできるのですが、この+ボタンを動かすことができません!どうせならJavaScriptで+ボタンも動くようにしておきましょう!

それではまずはHTMLを書いていきます!!

HTMLでアコーディオンのパーツを作っていきましょう。

まずはアコーディオンのパーツを作っていきます!

HTMLはこんな感じで書いていきます。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>FQAセクション</title>
    <!-- CSSファイル読み込み -->
    <link rel="stylesheet" href="css/style.css">
</head>
<body>

    <section class="qa sec">
      <h2 class="pageSubTitle pageSubTitle--center">
        FAQ
        <span>よくある質問</span>
      </h2>
        <div class="qa__box">
          <dl>
            <dt class="js-qa__parent"> <!-- jsで取得する要素 -->
                <span>Q</span>
                <h3 class="pageSubTitle2">大体の見積り金額を電話で教えてもらえますか?</h3>
                <div class="crossBar"></div>
            </dt>
            
            <dd class="qa__child">
                <span>A</span>
                <p class="contentText">テスト解答</p>
            </dd>
          </dl>
        </div>
        <div class="qa__box">
          <dl>
            <dt class="js-qa__parent">
                <span>Q</span>
                <h3 class="pageSubTitle2">もともと貼ってあるフィルムはいくらで剥がしてもらえますか?</h3>
                <div class="crossBar"></div>
            </dt>

            <dd class="qa__child">
                <span>A</span>
                <p class="contentText">テスト解答</p>
            </dd>
          </dl>
        </div>
        <div class="qa__box">
          <dl>
            <dt class="js-qa__parent">
                <span>Q</span>
                <h3 class="pageSubTitle2">途中でキャンセルもしくはフィルムの変更は可能ですか?</h3>
                <div class="crossBar"></div>
            </dt>

            <dd class="qa__child">
                <span>A</span>
                <p class="contentText">テスト解答</p>
            </dd>
          </dl>
        </div>
        <div class="qa__box">
          <dl>
            <dt class="js-qa__parent">
                <span>Q</span>
                <h3 class="pageSubTitle2">土日祝日でも施工していますか?</h3>
                <div class="crossBar"></div>
            </dt>

            <dd class="qa__child">
                <span>A</span>
                <p class="contentText">テスト解答</p>
            </dd>
          </dl>
        </div>
        <div class="qa__box">
          <dl>
            <dt class="js-qa__parent">
                <span>Q</span>
                <h3 class="pageSubTitle2">引越後でもガラスフィルムの施工は可能ですか?</h3>
                <div class="crossBar"></div>
            </dt>

            <dd class="qa__child">
                <span>A</span>
                <p class="contentText">テスト解答</p>
            </dd>
          </dl>
        </div>
        <div class="qa__box">
          <dl>
            <dt class="js-qa__parent">
                <span>Q</span>
                <h3 class="pageSubTitle2">現物サンプルを見たい時にはどうすれば良いですか?</h3>
                <div class="crossBar"></div>
            </dt>

            <dd class="qa__child">
                <span>A</span>
                <p class="contentText">テスト解答</p>
            </dd>
          </dl>
        </div>

    </section>

    <!-- JSファイルの読み込み -->
    <script src="js/index.js"></script>
</body>
</html>

JSファイルの読み込みとCSSファイルの読み込みをしておいてください。

FQAのパーツをdlタグで作ったのですが、中にあるdtタグの「js-qa__parent」というクラス名に注目してください僕の中のルールなんですが、JavaScriptでHTMLを取得するクラス名は頭に「js-」を付けています。このjs-から始まるクラス名にはJavaScriptの操作用のクラスになります。

アコーディオンのJavaScript部分は「js-qa__parent」のHTML要素を1つだけ取得するだけです。

HTMLが書けたら、次はCSSを書いていきたいと思います!!

CSSでスタイルを付けよう!

  body {
    background-color: #F4F6F6;
  }
  .sec {
    width: auto;
    padding: 0 20px;
  }
  .pageSubTitle {
    font-size: 40px;
    line-height: 1.75;
    font-weight: 900;
    font-style: normal;
    position: relative;
  }
  .pageSubTitle::after {
    content: "";
    display: block;
    width: 74.6px;
    height: 2px;
    margin: 0 auto;
    margin-top: 35.2px;
  }
  .pageSubTitle > span {
    display: block;
    font-size: 13px;
    line-height: 1.75;
    font-weight: 400;
    font-style: normal;
    margin-top: 5px;
  }
  .pageSubTitle--center {
    text-align: center;
  }
  
  /* FAQパーツ */
  .pageSubTitle2 {
    font-size: 23px;
    line-height: 1.7;
    font-weight: 400;
    font-style: normal;
    color: #4D4D4D;
  }
  .contentText {
    font-size: 23px;
    line-height: 1.7;
    font-weight: 400;
    font-style: normal;
    color: #4D4D4D;
  }
  .qa {
    padding-top: 180px;
    padding-bottom: 278px;
  }
  .qa__box {
    max-width: 1100px;
    width: 100%;
    margin: 0 auto;
    position: relative;
    z-index: 10;
  }
  .qa__box > dl > dt {
    background-color: #fff;
    display: flex;
    align-items: center;
    padding: 10px 30px;
    position: relative;
  }
  .qa__box > dl > dt .crossBar {
    width: 21px;
    height: 21px;
    position: absolute;
    top: 50%;
    right: 24px;
    transform: translateY(-50%);
  }
  .qa__box > dl > dt .crossBar::before {
    content: "";
    display: inline-block;
    width: 1.6px;
    height: 100%;
    background-color: #4D4D4D;
    position: absolute;
    top: 0;
    left: 50%;
    transform-origin: center center; /* 中心を基準にtransform */
    transform: translateX(-50%) rotate(0deg);
    transition: transform 0.8s cubic-bezier(0.19, 1, 0.22, 1);
  }
  .qa__box > dl > dt .crossBar::after {
    content: "";
    display: inline-block;
    width: 100%;
    height: 1.6px;
    background-color: #4D4D4D;
    position: absolute;
    top: 50%;
    left: 0;
    transform: translateY(-50%);
  }
  .qa__box > dl > dt > span {
    font-size: 28px;
    line-height: 1.75;
    font-weight: 500;
    font-style: normal;
    color: #5FB1DE;
    margin-right: 23px;
  }
  
  .qa__box > dl > dd {
    margin: 0;
    background-color: #fff;
    display: flex;
    align-items: center;
    margin-bottom: 16px;
    padding: 0px 30px; /* paddingの上下を0にする */
    max-height: 0; /* max-heightを0にする、レスポンシブ対応 */
    overflow: hidden;  /* overflow: hidden;を付けるのがポイント */
    transition: all 0.8s cubic-bezier(0.19, 1, 0.22, 1);
  }
  .qa__box > dl > dd > span {
    font-size: 28px;
    line-height: 1.7;
    font-weight: 500;
    font-style: normal;
    color: #5FB1DE;
    margin-right: 23px;
  }
  
  
  /* アニメーション後のスタイル */
  .qa__box > dl > dd.is-open { /* heightとpaddingをアニメーション */
    max-height: 100px;
    padding: 10px 30px;
  }
  /* +ボタンのアニメーション */
  .qa__box > dl > dt.is-open .crossBar::before {
    transform: translateX(-50%) rotate(90deg);  /* 90度回転 */
  }

CSSのポイントは閉じている状態では、max-heightとpaddingを0にしておいて、overflow: hidden;でテキストを見えないようにしておくことです。

なぜ、max-heightかというと、heightにしておくとスマホサイズでテキストが改行した時にはみ出す可能性があるので、max-heightにして大きめの数字にしておくことでそれを防止できます!

今の所これがシンプルで一番良いと思っております。(何気にここに辿り着くのにかなり試行錯誤してます、、)

アコーディオンはCSSがポイントなので、この辺りをしっかりマスターしておいてください!!

アニメーション部分はtransitionで設定して、アニメーション後のスタイルも設定しております。

+ボタンの設置は、divタグで正方形のボックスを作っておき、そのボックスに::before::after擬似要素でバーを2本作っています。この辺りは好きにカスタマイズしてもらってもいいと思います。

+ボタンのアニメーション後は::beforeの方だけ90度回転させた配置にします。

一応、ここでブラウザチェックをしておきましょう!!

Aの解答が見えないようになっていれば、正しく動いています。

それでは次はJavaScriptで動きの部分を作っていきたいと思います!!

JavaScriptでアコーディオンに動きをつけていきましょう!

アコーディオンにJavaScriptで動きをつけるポイントは、

  • FAQの項目は複数あるので複数設置できるように設定
  • まずQ部分をJavaScriptで取得して、Q部分から辿ってA部分を取得する

「まずQ部分をJavaScriptで取得して、Q部分から辿ってA部分を取得する」がイメージ湧きにくいと思いますが、JavaScriptでは取得したHTMLの「兄弟要素を取得する」ということができます。例えば、兄弟要素とはpタグの横にもう一つpタグが並んでたとしたら、その隣のpタグを取得するということです。

親でも子でもなく、隣のHTMLタグということです。

この2つのポイントを押さえつつ進めていきます。

HTMLを取得しよう

index.jsファイルに書いていきます!

'use strict';
{
    // アコーディオン
    // DOM取得
    const parent = document.querySelectorAll('.js-qa__parent');
    console.log(parent);

}

まずはjs-をつけたクラスを取得しておきます。js-を付けたクラスは複数あるので、querySelectorAll()を使っていきます。

一応、console.log(parent)でちゃんとHTMLを取得できているか?を確認しておきます。

NodeListでHTML要素を6つ取得できていれば成功です!

NodeListがわからないという人はこちらで解説してます。

取得したHTMLにclickイベントを付加しよう

次は取得したHTML要素に繰り返し構文のfor文で一つ一つにイベントを付けていきます。

'use strict';
{
    // アコーディオン
    // DOM取得
    const parent = document.querySelectorAll('.js-qa__parent');
    console.log(parent);

    // イベント
    for (let i = 0; i < parent.length; i++) {
        // clickイベントを付加
        parent[i].addEventListener('click', accodionToggle);
    }

}

addEventListenerでイベントを付加します。付けるイベントの種類はFAQでクリックした時に開くようにするので、clickイベントを設定しています。

addEventListenerの第二引数では、accodionToggleという関数名を設定して、この後関数を作っていきます。

addEvenetListenerや関数がわからない人はこちらの記事を読んでみてください。

次はaddEventListenerの中で使うaccodionToggleの関数の処理を書いていきます。

イベントの関数に処理を書いていこう

accodionToggleにどういう処理を書くか?というと、クリックした後、アコーディオンが開く動きを書いていきます。

index.jsに処理を追加していきます。

'use strict';
{
    // アコーディオン
    // DOM取得
    const parent = document.querySelectorAll('.js-qa__parent');
    console.log(parent);

    // イベント
    for (let i = 0; i < parent.length; i++) {
        // clickイベントを付加
        parent[i].addEventListener('click', accodionToggle);
    }

    // 処理
    function accodionToggle(e) {
        // クリックした要素にクラスを付加
        e.currentTarget.classList.toggle('is-open');
        // クリックした要素の兄弟要素を取得
        const child = e.currentTarget.nextElementSibling;
        // 兄弟要素にクラスを付加
        child.classList.toggle('is-open');
    }
}

まずはfunction accodionToggle(e)と書きます。カッコの中に引数eを書いています。実はaddEventListenerは引数を受け取れます。

どんな引数を受け取れるかというと、イベントが発生したHTMLの情報が受け取れます。e.currentTargetと繋げて書いていくことで、イベントが発生したHTMLを個別で取得できます。

例えば、下記画像では一つだけFAQが開いてますが、この開いている部分だけの赤枠部分のHTMLをe.currentTargetと書くことで取得できます。つまりe.currentTargetはクリックしてイベントが発生した要素の取得ができます。

まずはe.currentTargetでクリックした要素にclassListでis-openクラスを付加します。

次はこの部分です。

// クリックした要素の兄弟要素を取得
const child = e.currentTarget.nextElementSibling;

e.currentTargetに.nextElementSiblingと繋げて書くことで、クリックしたHTMLの次の兄弟要素を取得できます。次とは後に並んで書いてあるHTML要素のことです。

この部分ですが、一旦、変数childを作って、そこにクリックした要素の兄弟要素を取得して変数childに格納します。

なぜ一旦変数に格納するのかというと、

e.currentTarget.nextElementSibling.classList.toggle('is-open');

と書けないからです。classListに繋げれるのはHTML要素が入った変数のみに.classListを繋げれます。

HTMLで見るとこういう動きをしています。

ちなみに前の兄弟要素を取得するときは、previousElementSiblingというのを使います。あまり使ったことないですがw

後の兄弟要素を取得したら、その兄弟要素にもclassListでis-openクラスを付加します。

この部分ですね。

// 兄弟要素にクラスを付加
child.classList.toggle('is-open');

これで兄弟要素にもクラスを付加できました!!

これでブラウザチェックをしてみましょう!CSSでアニメーションの処理を書いているのでもう動くはずです。

このようにFAQの項目が独立してちゃんと動いていれば、これで完了になります!!

もし動かなかった人がいれば、デュフフを使ってコードを差分チェックをしてもらうと間違っている部分をあぶり出せます。

もしそれでもちゃんと動かない場合は、コードファイルをダウンロードできますので、それで確認してみてください。

jQueryでFAQを実装する(7行で完了)

jQueryでFAQを実装すると、実はJSは7行で完了できます。

こちらの記事もアップしましたので、jQueryで数行でサッと書きたい人は参考にしていただければと思います。

JavaScriptを学習したいなら講座を用意しています

ここまで読み進めていただけた人はJavaScriptを学んでいきたいと思っている人が多いのではないかと思います。

そんな人のためにホームページ制作で使うJavaScriptを網羅した講座を用意していますので、この講座をフルで活用していただければと思います。

ホームページ制作で使うJavaScript講座

もし実装などで上手くいかないことありましたら、下記ボタンからご相談いただけましたらありがたいです。

この記事を書いた人

アバター

トノムラマサシ

masashi
Webサイト屋兼ブロガー|過去に企業常駐などを経て現在はフリーランスでディレクションとコーダーとして活動しております。JS大好きなのでJSの仕事依頼お待ちしております。京都在中で5人家族。