Written by Toshiki

アコーディオンの実装を紹介(jQuery使用)

Web制作 プログラミング

こんにちは、トシキです。
今回はアコーディオンの実装を紹介します。
忍者CODEさんのライブコーディングで実装した、アコーディオンに改良を加えたので備忘録として残します。

本記事の内容

コードの解説

コードの解説

ライブコーディングした時との違いはざっくり2つです。

  • 質問文<dt>のエリアだけではなく、回答文<dd>のエリアをクリックしてもアコーディオンが閉じるようにした
  • アローの擬似要素を画像からCSSの実装に変更

デザインは変わりませんが、テキストはダミーに変えています。

コードの解説

ライブコーディングでは説明を忘れていましたが、CSS設計はPRECSS手法を採用しました。
こちらが参考にした書籍です↓
CSS設計完全ガイド ~詳細解説+実践的モジュール集

HTML

  
    <section class="ly_section">
      <div class="ly_section_inner">
        <h2 class="el_lv2Heading">アコーディオンのセクション</h2>
        <!-- アコーディオン -->
        <dl class="bl_accordion">
          <div class="bl_accordion_inner">
            <dt>
              <button class="bl_accordion_btn" type="button">質問文のテキストが入ります。</button>
            </dt>
            <dd>
              <button class="bl_accordionBtn_close" type="button">回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。<br>回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。</button>
            </dd>
          </div>
          <div class="bl_accordion_inner">
            <dt>
              <button class="bl_accordion_btn" type="button">質問文のテキストが入ります。</button>
            </dt>
            <dd>
              <button class="bl_accordionBtn_close" type="button">回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。<br>回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。</button>
            </dd>
          </div>
          <div class="bl_accordion_inner">
            <dt>
              <button class="bl_accordion_btn" type="button">質問文のテキストが入ります。</button>
            </dt>
            <dd>
              <button class="bl_accordionBtn_close" type="button">回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。<br>回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。</button>
            </dd>
          </div>
          <div class="bl_accordion_inner">
            <dt>
              <button class="bl_accordion_btn" type="button">質問文のテキストが入ります。</button>
            </dt>
            <dd>
              <button class="bl_accordionBtn_close" type="button">回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。<br>回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。</button>
            </dd>
          </div>
        </dl>
        <!-- アコーディオンここまで -->
      </div>
    </section>
  

<dd>直下にも<button>を追加

改良前は展開済みのアコーディオンを閉じる時、回答文のエリアはクリックしても閉じない設計でした。
ですので今回の改良版では、閉じる時のクリックエリアを回答文まで広げた方が良さそうだと思い追加しました。

CSS

  
    /* リセット */
    html {
      font-size: 62.5%;
    }
    button {
      background-color: #FFF;
      border: none;
      margin: 0;
      outline: none;
      padding: 0;
    }
    dd {
      margin: 0;
    }
    *::before {
      box-sizing: border-box;
    }

    /* セクション */
    .ly_section {
      padding: 72px 0;
    }
    .ly_section_inner {
      margin: 0 auto;
      max-width: 1010px;
      padding: 0 15px;
      text-align: center;
    }
    .el_lv2Heading {
      color: #28BE78;
      font-size: 2.6rem;
      font-weight: 700;
      line-height: 36px;
      text-align: center;
    }

    @media (max-width: 767px) {
      .ly_section_inner {
        padding: 0 4vw;
      }
      .el_lv2Heading {
        font-size: 2.2rem;
        line-height: 30px;
      }
    }

    /* ---------------------------------------- */
    /* アコーディオン */
    /* ---------------------------------------- */
    .bl_accordion {
      margin: 24px 0;
    }
    .bl_accordion_inner dt {
      border-top: 1px solid #E7E7E7;
    }

    /* 質問文[dt]直下のボタン */
    .bl_accordion_btn {
      color: #3A3A3A;
      cursor: pointer;
      display: block;
      font-size: 1.6rem;
      font-weight: 700;
      line-height: 24px;
      padding: 25px 40px 23px 0;
      position: relative;
      text-align: left;
      transition: color .4s;
      width: 100%;
    }

    /* 擬似要素のアロー */
    .bl_accordion_btn::before {
      content: "";
      position: absolute;
      top: 27px;
      right: 0;
      width: 14px;
      height: 14px;
      border-bottom: 2px solid #A8A8A8;
      border-left: 2px solid #A8A8A8;
      transform: rotate(-45deg);
      transition: all .4s;/* [transform]と[border-color]に適用 */
    }

    /* [dd](回答文)の初期値は非表示 */
    .bl_accordion dd {
      display: none;
    }

    /* 回答文[dd]直下のボタン */
    .bl_accordionBtn_close {
      color: #3A3A3A;
      cursor: pointer;
      display: block;
      line-height: 21px;
      padding-bottom: 24px;
      text-align: left;
      transition: color .4s;
      width: 100%;
    }

    /* 以下3箇所ホバー時のカラー変更はPCのみ適用
    →今回はデザインカンプのコンテンツ幅が980pxだったので、
    それ以上をPC扱いに指定 */
    @media (min-width: 980px) {
    /* 質問文のテキストカラー */
    .bl_accordion_inner:hover .bl_accordion_btn {
      color: #28BE78;
    }
    /* アローのカラー */
    .bl_accordion_inner:hover .bl_accordion_btn::before {
      border-color: #28BE78;
    }
    /* 回答文のテキストカラー */
    .bl_accordion_inner:hover .bl_accordionBtn_close {
      color: #28BE78;
      }
    }

    /* [.is_active]クラスが付与されたらアローを回転 */
    .bl_accordion_btn.is_active::before {
      transform: rotate(135deg);
    }
  

<button>タグについて

初期値はdisplay: inline-block;なので、
display: block;width: 100%;で幅いっぱいに伸ばしています。
ひとつ目の<button>(.bl_accordion_btn)の方は質問文が長くなった場合、擬似要素のアローに重ならないようにpadding-right: 40px;を指定しています。

擬似要素のアローについて

背景画像からborderを使った実装に変更しました。
border-bottomとborder-leftに2px solid #A8A8A8を設定。

CSS

transform: rotate(-45deg);で傾けます。

CSS

.bl_accordion ddについて

回答文の初期値はdisplay: none;で非表示にします。
後に解説するjQueryのslideToggleとslideUpの切り替えで使います。

jQuery

  
    // アコーディオンのクリックイベント
    $(function () {
      // 質問文のエリアがクリックされた時
      $('.bl_accordion_inner > dt').on('click', function () {
        $(this).children('.bl_accordion_btn').toggleClass('is_active');// ①
        $(this).next('dd').slideToggle();// ②
      });

      // 回答文のエリアがクリックされた時
      $('.bl_accordion_inner > dd').on('click', function () {
        $(this).prev('dt').children('.bl_accordion_btn').removeClass('is_active');// ③
        $(this).slideUp();// ④
      });
    })
  

①について

<dt>がクリックされた時、
この$(this)要素の子要素.children(.bl_accordion_btn)に、.toggleClass(.is_active)を追加・削除します。

②について

<dt>がクリックされた時、
この$(this)要素直後の兄弟要素.next(dd)に、.slideToggleを適用します。
対象の要素が非表示display: none;ならばdisplay: block;に。
表示状態ならdisplay: none;で非表示にします。
今回は<dd>の初期値にdisplay: none;していますが、指定していなければ逆の動作になります。

③について

<dd>がクリックされた時、
この$(this)要素直前の兄弟要素.prev(dt)の、子要素.children(.bl_accordion_btn)から、.removeClassで.is_activeを削除します。

④について

<dd>がクリックされた時、
この$(this)要素を非表示.slideUpにします。

終わりに

終わりに

アコーディオンはWeb制作でよく使う機能ですので、今回もし参考になりましたら幸いです!
最後に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>アコーディオン</title>
      <style>
        /* リセット */
        html {
          font-size: 62.5%;
        }

        button {
          background-color: #FFF;
          border: none;
          margin: 0;
          outline: none;
          padding: 0;
        }

        dd {
          margin: 0;
        }

        *::before {
          box-sizing: border-box;
        }

        /* セクション */
        .ly_section {
          padding: 72px 0;
        }

        .ly_section_inner {
          margin: 0 auto;
          max-width: 1010px;
          padding: 0 15px;
          text-align: center;
        }

        .el_lv2Heading {
          color: #28BE78;
          font-size: 2.6rem;
          font-weight: 700;
          line-height: 36px;
          text-align: center;
        }

        @media (max-width: 767px) {
          .ly_section_inner {
            padding: 0 4vw;
          }

          .el_lv2Heading {
            font-size: 2.2rem;
            line-height: 30px;
          }
        }

        /* ---------------------------------------- */
        /* アコーディオン */
        /* ---------------------------------------- */
        .bl_accordion {
          margin: 24px 0;
        }

        .bl_accordion_inner dt {
          border-top: 1px solid #E7E7E7;
        }

        /* 質問文[dt]直下のボタン */
        .bl_accordion_btn {
          color: #3A3A3A;
          cursor: pointer;
          display: block;
          font-size: 1.6rem;
          font-weight: 700;
          line-height: 24px;
          padding: 25px 40px 23px 0;
          position: relative;
          text-align: left;
          transition: color .4s;
          width: 100%;
        }

        /* 擬似要素のアロー */
        .bl_accordion_btn::before {
          content: "";
          position: absolute;
          top: 27px;
          right: 0;
          width: 14px;
          height: 14px;
          border-bottom: 2px solid #A8A8A8;
          border-left: 2px solid #A8A8A8;
          transform: rotate(-45deg);
          transition: all .4s;
          /* [transform]と[border-color]に適用 */
        }

        /* [dd](回答文)の初期値は非表示にしてjQueryの
          →slideToggleで[display: block;]と切り替える
          →slideUpで[display: block;]を外して[display: none;]に戻す */
        .bl_accordion dd {
          display: none;
        }

        /* 回答文[dd]直下のボタン */
        .bl_accordionBtn_close {
          color: #3A3A3A;
          cursor: pointer;
          display: block;
          line-height: 21px;
          padding-bottom: 24px;
          text-align: left;
          transition: color .4s;
          width: 100%;
        }

        /* 以下3箇所ホバー時のカラー変更はPCのみ適用
          →今回はデザインカンプのコンテンツ幅が980pxだったので、
          それ以上をPC扱いに指定(明確な決まり無し) */
        @media (min-width: 980px) {

          /* 質問文のテキストカラー */
          .bl_accordion_inner:hover .bl_accordion_btn {
            color: #28BE78;
          }

          /* アローのカラー */
          .bl_accordion_inner:hover .bl_accordion_btn::before {
            border-color: #28BE78;
          }

          /* 回答文のテキストカラー */
          .bl_accordion_inner:hover .bl_accordionBtn_close {
            color: #28BE78;
          }
        }

        /* [.is_active]クラスが付与されたらアローを回転 */
        .bl_accordion_btn.is_active::before {
          transform: rotate(135deg);
        }
      </style>
    </head>
    <body>
      <section class="ly_section">
        <div class="ly_section_inner">
          <h2 class="el_lv2Heading">アコーディオンのセクション</h2>
          <!-- アコーディオン -->
          <dl class="bl_accordion">
            <div class="bl_accordion_inner">
              <dt>
                <button class="bl_accordion_btn" type="button">質問文のテキストが入ります。</button>
              </dt>
              <dd>
                <button class="bl_accordionBtn_close">回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。<br>回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。</button>
              </dd>
            </div>
            <div class="bl_accordion_inner">
              <dt>
                <button class="bl_accordion_btn" type="button">質問文のテキストが入ります。</button>
              </dt>
              <dd>
                <button class="bl_accordionBtn_close">回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。<br>回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。</button>
              </dd>
            </div>
            <div class="bl_accordion_inner">
              <dt>
                <button class="bl_accordion_btn" type="button">質問文のテキストが入ります。</button>
              </dt>
              <dd>
                <button class="bl_accordionBtn_close">回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。<br>回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。</button>
              </dd>
            </div>
            <div class="bl_accordion_inner">
              <dt>
                <button class="bl_accordion_btn" type="button">質問文のテキストが入ります。</button>
              </dt>
              <dd>
                <button class="bl_accordionBtn_close">回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。<br>回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。回答文のテキストが入ります。</button>
              </dd>
            </div>
          </dl>
          <!-- アコーディオンここまで -->
        </div>
      </section>
      <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
      <script>
        // アコーディオンのクリックイベント
        $(function () {
          // 質問文のエリアがクリックされた時
          $('.bl_accordion_inner > dt').on('click', function () {
            $(this).children('.bl_accordion_btn').toggleClass('is_active');// ①
            $(this).next('dd').slideToggle();// ②
          });

          // 回答文のエリアがクリックされた時
          $('.bl_accordion_inner > dd').on('click', function () {
            $(this).prev('dt').children('.bl_accordion_btn').removeClass('is_active');// ③
            $(this).slideUp();// ④
          });
        })
      </script>
    </body>
    </html>
  

人気記事忍者CODEさんのコーディング案件に挑戦してみた

人気記事WordPressのトップにHTMLページを表示させる方法