KEPRATE
  • TOP トップページ
  • ABOUT KEPRATEについてKEPRATEについて
  • BLOG ブログ
    BLOG ブログ
    画像:今ならまだ間に合う! CSSのposition要素の新刺客 position: sticky; を使おう!

    今ならまだ間に合う! CSSのposition要素の新刺客 position: sticky; を使おう!

    2023.07.24
    画像:WordPressのテーマを自作しよう! ページネーション(ページ送り)をプラグインなしで実装する方法

    WordPressのテーマを自作しよう! ページネーション(ページ送り)をプラグインなしで実装する方法

    2023.07.14
    画像:WordPressのテーマを自作しよう! get_previous_post()を使って同カテゴリ内の前後リンクを取得する方法

    WordPressのテーマを自作しよう! get_previous_post()を使って同カテゴリ内の前後リンクを取得する方法

    2023.07.11
    画像:WordPressのテーマを自作しよう! タグ一覧ページ・タグクラウド作成方法 タグ関連はこれを読めば大丈夫!

    WordPressのテーマを自作しよう! タグ一覧ページ・タグクラウド作成方法 タグ関連はこれを読めば大丈夫!

    2023.07.10
  • LAB ラボ
    LAB ラボ
    画像:KEP TYPING KEYBOARD
    KEP TYPING KEYBOARD
    公開日
    2017.07.01
    画像:KEP SLOT
    KEP SLOT
    公開日
    2017.07.01
    画像:KEP DOT DRAW
    KEP DOT DRAW
    公開日
    2017.07.01
  • FAQ よくあるご質問よくあるご質問
  • NEWS お知らせ
  • CONTACT お問い合わせ
    CONTACT お問い合わせ
    画像:ショップ 担当者様
    SHOP ショップ 担当者様
    画像:制作会社 担当者様
    COMPANY 制作会社 担当者様

【2023年最新版】HTMLのアコーディオンはdetailsやsummaryを使おう!

Satoken Satoken
48の必殺WEB
2023.07.03
  • JavaScript
  • CSS
  • HTML
画像:【2023年最新版】HTMLのアコーディオンはdetailsやsummaryを使おう!
ハメカメ ハメカメ

サトケン
Webサイトでアコーディオン(トグル)を実装する時はどうしている?

Satoken サトケン

ハメカメ師匠。
そうですね。。

昔はjQueryのslideToggle()を使ったり・・・
最近はslideToggle()を使わずに自作でアコーディオンを構築したりしていますが・・・。

ハメカメ ハメカメ

むははは。 いいじゃろう。
脱jQueryは意識しておるのだな・・・

それでも問題なく動作はするであろうが
<details>や<summary>は使用しているか?

Satoken サトケン

<details>!? <summary>!?

なんですか? そのタグは?

ハメカメ ハメカメ

フフフ・・

20万WEBパワーの弱小WEBコーダーじゃのう・・・・

Satoken サトケン

がーん・・・・!!

20万WEBパワー!! 弱小WEBコーダー!?

ハメカメ ハメカメ

フフフ・・

よかろう お前に今から
48の必殺WEBの一つ
「汎用性アコーディオン」を伝授しよう!

目次
  • 01.そもそもアコーディオンとは?
  • 02.<details>と<summary>について
  • 03.汎用性アコーディオンとは
  • 04.HTMLの記述
  • 05.CSSの記述
  • 06.JSの記述
  • 07.まとめ

そもそもアコーディオンとは?

HTMLのアコーディオン(トグル)とは、ウェブページ上で情報をコンパクトに表示するための便利な機能です。 アコーディオンはタイトル(ボタン)と、コンテンツで構成されタイトル(ボタン)をクリックすることによりコンテンツ部分が表示・非表示に切り替わることからアコーディオン(トグル)と表現されます。 HTMLでマークアップしてCSSでデザインをカスタマイズしJavaScriptで動作させます。

chotGPT書房刊
「あぁ青春の我がHTMLアコーディオン」より

<details>と<summary>について

IE11が2022年6月15日にサポートが終了し 2023年2月14日(日本時間2月15日)以降は使用できなくなったことにより今まで使用を控えていたタグである<details>と<summary>が使えるようになり新しいアコーディオンの選択肢となりました。

    HTML
    コピー
<details>
    <summary>アコーディオンのタイトルが入ります。</summary>
    アコーディオンのコンテンツのテキストになります。<br>
    アコーディオンのコンテンツのテキストになります。
</details>

<details>を使用したシンプルなアコーディオン

アコーディオンのタイトルが入ります。(クリックすると開きます。) アコーディオンのコンテンツのテキストになります。
アコーディオンのコンテンツのテキストになります。

最低限のアコーディオンの開く・閉じるの動作をCSSハックやJavascriptの記述なしで実現できます。
ただメリット・デメリットもあります。

メリット

以下メリットになります。

  • ・コードを見ただけで、極度の鈍感・近眼な方を除いてひと目見ただけでアコーディオンする箇所と認識できる。
  • ・アクセシビリティ面・ユーザビリティ面に優れている

    タブフォーカスとエンターキー・スペースキーでの開閉操作がデフォルトで狩野英孝状態。

デメリット

  • ・連打対策が必要(JSで対応)

    <details>タグのopen属性の機能で開く・閉じるの動きを素早くするとアニメーション動作が一瞬チカる。

  • ・Safari対策が必要(CSSで対応)

    <summary>タグでbefore・afterといったCSS疑似要素をtransform: rotate()で回転させようとすると回転しないバグあり。
    @keyframesで作成したアニメーションで回避もできますが作りがややこしくなるのでHTML上無駄なタグが増えますが今回は<span>タグで回避。
    IE11対策が必要なくなっても今後はSafariに一番悩まされそうな気配・・・

  • ・アコーディオンが開く際、必ず<summary>(タイトル)の下に開くという仕様がある。

    上部にテキストがあり下部に「もっと見る」「MORE」ボタンがありクリックすることにより隠れている下部のテキストを表示させるということが仕様上難しい。(CSSで余白を下部につくり<summary>をposition:absolute;で強引に下部に配置することは可能だが動きが微妙になってしまいます。 要検討かつ要健闘。)

汎用性アコーディオンとは

上記のような作成時にデメリットもありますがそのデメリットにしっかり対応したものを一度キチンと作成してCSSとJavascriptを変更することなく、<div><a><button>を<details>><summary>に差し替えても問題なく動作するアコーディオンのことをいいます。
(※勝手に命名しているだけです・・・)

サンプルを見るSAMPLE
  • ※なんと今回先着100,000名様にテンプレートとなるファイルはダウンロード可能です。
  • ※サンプルのソースコードは自由にお使いいただけますが、自己責任においてご使用ください。
  • ※hoge-itemの箇所をお好みの名前に適宜変更してお使いください。

HTMLの記述

通常版

<div>と<a>・<button>でマークアップ

    HTML
    コピー
<div class="hoge-items">
    <div class="hoge-item js-toggle-item">
        <a href="#" class="toggle-btn">divタイプのアコーディオンのタイトルが入ります。<span class="toggle-icon"></a>
        <div class="hoge-item__con toggle-con">
            <div class="hoge-item__inner">
                <p class="hoge-item__txt">divタイプアコーディオンのコンテンツのテキストになります。</p>
                <p class="hoge-item__txt">divタイプとdetailsタイプと同じ動きになりますね。HTMLのコードもほぼ同じです。</p>
            </div>
        </div>
    </div>
</div>

<details>と<summary>でマークアップ

    HTML
    コピー
<div class="hoge-items">
    <details class="hoge-item js-toggle-item">
        <summary class="toggle-btn">detailsタイプのアコーディオンのタイトルが入ります。<span class="toggle-icon"><span></span><span></span></span></summary>
        <div class="hoge-item__con toggle-con">
            <div class="hoge-item__inner">
                <p class="hoge-item__txt">detailsタイプアコーディオンのコンテンツのテキストになります。</p>
                <p class="hoge-item__txt">divタイプとdetailsタイプと同じ動きになりますね。HTMLのコードもほぼ同じです。</p>
            </div>
        </div>
    </details>
</div>

2,3,10行目のHTMLのタグを入れ替え
<summary>の場合はSafari対策として<span class="toggle-icon"><span></span><span></span></span>に変更します。
それだけで汎用性アコーディオンは同じ挙動となります。

要するにアコーディオンの要素(ボタンとコンテンツ)をjs-toggle-itemで囲みその下の階層に同列でボタンtoggle-btnとコンテンツtoggle-conを配置します。

─ js-toggle-item
── toggle-btn
── toggle-con

決め事はjs-toggle-item要素の中にtoggle-btnとtoggle-conを配置するだけ。
HTMLタグは<div>でも<summary>どちらでも問題ございません。

なぜ「js-toggle-item」だけ頭に「js-」をつけているのかというとこの「js-toggle-item」をもとにJavascript側で操作しているからです。 jsでこのタグを使用しているのをhtml上でも分かりやすくするための「js-」になります。 接頭語と呼ばれています。

最初から開いた状態

<div>と<a>・<button>でマークアップ

    HTML
    コピー
<div class="hoge-items">
    <div class="hoge-item js-toggle-item state-active">
        <a href="#" class="toggle-btn">divタイプのアコーディオンのタイトルが入ります。<span class="toggle-icon"></a>
        <div class="hoge-item__con toggle-con">
            <div class="hoge-item__inner">
                <p class="hoge-item__txt">divタイプアコーディオンのコンテンツのテキストになります。</p>
                <p class="hoge-item__txt">divタイプとdetailsタイプと同じ動きになりますね。HTMLのコードもほぼ同じです。</p>
            </div>
        </div>
    </div>
</div>

js-toggle-itemにstate-activeクラスを付加する

  • ※独自でJSで作成している機能になります。

<details>と<summary>でマークアップ

    HTML
    コピー
<div class="hoge-items">
    <details class="hoge-item js-toggle-item" open>
        <summary class="toggle-btn">detailsタイプのアコーディオンのタイトルが入ります。<span class="toggle-icon"><span></span><span></span></span></summary>
        <div class="hoge-item__con toggle-con">
            <div class="hoge-item__inner">
                <p class="hoge-item__txt">detailsタイプアコーディオンのコンテンツのテキストになります。</p>
                <p class="hoge-item__txt">divタイプとdetailsタイプと同じ動きになりますね。HTMLのコードもほぼ同じです。</p>
            </div>
        </div>
    </details>
</div>

<details>にopen属性を付加する

  • ※<details>のデフォルトの機能になります。

CSSの記述

三角形アイコンを非表示に・カーソルを指マークに変更

デフォルトCSS対策とSafari対策が必要になります。

    CSS
    コピー
summary {
  /* display: list-item;以外を指定してデフォルトの三角形アイコンを消す しかしSafariにはききません。。 */
  display: block;
  /* デフォルトで指マークになっていないのでcursorをpointerに変更 */
  cursor: pointer;
}

summary::-webkit-details-marker {
  /* 不屈の闘志でSafariで表示されるデフォルトの三角形アイコンを消します */
  display: none;
}

summary{
    cursor: pointer;
}

三角形アイコンが非表示になり、指マークに変更された<details>と<summary>になります。 下記が変更後のアコーディオンになります。

アコーディオンのタイトルが入ります。(クリックすると開きます。) アコーディオンのデフォルトのにっくき三角形アイコンが消えてしまいました。
アコーディオンのデフォルトのにっくき三角形アイコンが消えてしまいました。

コンテンツ部分を非表示に

    CSS
    コピー
.toggle-con {
    /* アコーディオンで表示・非表示を切り替えられるコンテンツ部分を非表示の状態にする */
    overflow: hidden;
    height: 0;
}

アニメーションさせるアコーディオン作成時に<details>と<summary>を使用する・しない関係なしにコンテンツ部分を非表示にする必要があります。

  • ・三角形アイコンを非表示に・カーソルを指マークに変更
  • ・コンテンツ部分を非表示に

上記の2点がアコーディオン作成時に必ず必要になるCSSであとはアコーディオンに対するデザイン調整のCSSになります。

ただし注意点が一点だけあります。
コンテンツ箇所の余白を取る時、<toggle-con>(コンテンツ箇所の一番上の階層)で余白をとるのではなく <toggle-item__inner>(その一つ下の階層)で余白をとるようにしてください。

    HTML
    コピー
<div class="hoge-items">
    <details class="hoge-item js-toggle-item">
        <summary class="toggle-btn">アコーディオンのタイトルが入ります。<i class="toggle-icon"></i></summary>
        <div class="hoge-item__con toggle-con"> ←こちらではなく
            <div class="hoge-item__inner"> ←こちらで余白を取る
                <p class="hoge-item__txt">アコーディオンのコンテンツのテキストになります。</p>
                <p class="hoge-item__txt">アコーディオンのコンテンツのテキストになります。アコーディオンのコンテンツのテキストになります。</p>
            </div>
        </div>
    </details>
</div>

余白をとる箇所を間違えてしまうとアコーディオンアニメーション時に挙動がガクつく感じになってしまいます。 この一点だけ気をつけてあとは自分のお好みのデザインのアコーディオンを作成してください。

アコーディオン箇所のサンプルCSS

参考までに下記がKEPRATEサイトのFAQで使用しているのデザインのアコーディオンとなります。

    CSS
    コピー
summary::-webkit-details-marker {
  /* Safariで表示されるデフォルトの三角形アイコンを消します */
  display: none;
}

summary {
  /* カーソルは指マークに */
  cursor: pointer;
}

.toggle-con {
  /* アコーディオンで表示・非表示を切り替えられるコンテンツ部分を非表示の状態にする */
  overflow: hidden;
  height: 0;
}

.hoge-items {
  width: 100%;
  margin: 0 auto;
  text-align: left;
}
.hoge-items .hoge-item {
  border-bottom: 1px solid #ccc;
}
.hoge-items .hoge-item:last-child {
  border-bottom: none;
}

/* safari */
.hoge-item.state-active .toggle-btn .toggle-icon span:first-child, .hoge-item.state-active .toggle-btn .toggle-icon:after {
  transform: rotate(90deg);
}
.hoge-item .toggle-btn .toggle-icon span:first-child, .hoge-item .toggle-btn .toggle-icon:after {
  transform: rotate(0deg);
}
.hoge-item summary.toggle-btn .toggle-icon::after, .hoge-item summary.toggle-btn .toggle-icon::before {
  display: none;
}
.hoge-item .toggle-btn {
  position: relative;
  z-index: 1;
  display: block;
  padding: 15px 25px 15px 45px;
  font-size: 14px;
  font-weight: 500;
  line-height: 1.8;
}
@media print, screen and (min-width: 768px) {
  .hoge-item .toggle-btn {
    padding: 44px 100px 44px 80px;
    font-size: 20px;
  }
}
.hoge-item .toggle-btn::after {
  content: 'Q';
  position: absolute;
  display: block;
  left: 15px;
  font-weight: 500;
  font-size: 22px;
  z-index: 2;
  color: #a61f24;
  top: 50%;
  transform: translateY(-50%);
}
@media print, screen and (min-width: 768px) {
  .hoge-item .toggle-btn::after {
    left: 26px;
    font-size: 40px;
  }
}
.hoge-item .toggle-btn .toggle-icon {
  position: absolute;
  top: 50%;
  right: 2.66667vw;
  width: 12px;
  height: 12px;
  margin-top: -6px;
}
@media print, screen and (min-width: 768px) {
  .hoge-item .toggle-btn .toggle-icon {
    width: 20px;
    height: 20px;
    margin-top: -10px;
    right: 20px;
  }
}
.hoge-item .toggle-btn .toggle-icon span:last-child, .hoge-item .toggle-btn .toggle-icon::before {
  display: block;
  position: absolute;
  top: 50%;
  margin-top: -1px;
  left: 0;
  width: 100%;
  height: 2px;
  background: #111;
  content: "";
  z-index: 1;
  transition: transform 0.3s 0s ease;
}
.hoge-item .toggle-btn .toggle-icon span:first-child, .hoge-item .toggle-btn .toggle-icon::after {
  display: block;
  position: absolute;
  top: 0;
  margin-left: -1px;
  left: 50%;
  width: 2px;
  height: 100%;
  background: #111;
  content: "";
  z-index: 2;
  transition: transform 0.3s 0s ease;
}

.hoge-item__inner {
  padding: 20px 20px 20px 45px;
  font-size: 14px;
  font-weight: 400;
  line-height: 2;
  position: relative;
  background: #f4f4f4;
  border-radius: 16px;
  overflow: hidden;
  margin-bottom: 20px;
}
@media print, screen and (min-width: 768px) {
  .hoge-item__inner {
    padding: 40px 100px 40px 80px;
    font-size: 16px;
    border-radius: 16px;
    margin-bottom: 40px;
  }
}
.hoge-item__inner::after {
  content: 'A';
  position: absolute;
  display: block;
  left: 15px;
  font-weight: 500;
  font-size: 22px;
  z-index: 2;
  color: #333;
  top: 12px;
}
@media print, screen and (min-width: 768px) {
  .hoge-item__inner::after {
    left: 26px;
    font-size: 40px;
    top: 16px;
  }
}
.hoge-item__inner .hoge-item__txt {
  font-size: 14px;
  line-height: 1.8;
  margin-bottom: 10px;
}
@media print, screen and (min-width: 768px) {
  .hoge-item__inner .hoge-item__txt {
    font-size: 18px;
    margin-bottom: 12px;
  }
}
.hoge-item__inner .hoge-item__txt:last-child {
  margin-bottom: 0;
}

JSの記述

今回JSアニメーションライブラリはGSAPを使用しております。

<details>はデフォルトの機能として<open>属性の有無でコンテンツ部分を表示・非表示としているようです。 この機能のせいで表示・非表示が一瞬で行われてしまい、JSでアニメーションを実装する際に必要のない機能となります。
JSでは以下の作業がポイントとなります。

  • ・<open>がデフォルトのタイミングで発動しないように<summary>要素をクリック時にデフォルトの機能を無効化させるpreventDefault()
  • ・open属性を任意のタイミングで追加・削除(アニメーション完了時)する
  • ・<open>のデフォルトの機能のため、連打すると挙動がおかしくなるので対策が必要

デフォルトの機能を無効化

    JS
    コピー
const toggleClick = (e) => {

  e.preventDefault(); //デフォルトの動作をキャンセル

}

toggleBtn.addEventListener("click", toggleClick);

クリック時にe.preventDefault();でデフォルトの動作をキャンセルします。

アニメーション完了時にopen属性を削除

    JS
    コピー
gsap.to(toggleCon, {
  height: toggleH,
  ease: toggleEase,
  duration: toggleT,
  onComplete: () => {
    //アコーディオンのアニメーション完了時
    //高さが0の場合 = アコーディオンを閉じた場合
    if (toggleH == 0) toggle.removeAttribute("open");
  }
})

アコーディオンアニメーションが終了したタイミングでアコーディオンの高さが0の時はremoveAttribute("open");を実行します。

連打対策

    JS
    コピー
//もしstate-animateクラスがあれば(アニメーションが終了していない状態)移行の処理は実行させない
if (toggle.classList.contains('state-animate')) {
  return;
}

//アニメーションが始るタイミングでstate-animateクラスを付加
toggle.classList.add('state-animate');

gsap.to(toggleCon, {
  height: toggleH,
  ease: toggleEase,
  duration: toggleT,
  onComplete: () => {
    //アニメーションが終了したタイミングでstate-animateクラスを削除
    toggle.classList.remove('state-animate');
  }
})

state-animateクラスの有無でアニメーションが終了していない場合は移行の処理を実行させないようにする

以下JS全ソースになります。

─ js-toggle-item
── toggle-btn
── toggle-con

JS内で使用されている要素は上記だけでありHTML内のその他の要素の影響がない作りになっているのが確認できるかと思われます。 あとはCSSでサイトに適したデザイン調整作業に集中することが可能です。

    JS
    コピー
const toggleFunction = () => {

  const toggleItems = document.querySelectorAll(".js-toggle-item");
  const toggleItemsLen = toggleItems.length;
  const breakPoint = 768; //任意で変更してください

  for (let i = 0; i < toggleItemsLen; i++) {
    const toggle = toggleItems[i];
    const toggleBtn = toggle.getElementsByClassName('toggle-btn')[0];
    const toggleCon = toggle.getElementsByClassName('toggle-con')[0];

    if(toggle.open) toggle.classList.add('state-active');

    if (toggle.classList.contains('state-active')) {
      toggleCon.style.height = 'auto';
    } else {
      toggleCon.style.height = 0;
    }

    const toggleClick = (e) => {

      e.preventDefault(); //デフォルトの動作をキャンセル

      if (toggle.classList.contains('state-animate')) {
        return;
      }

      let toggleH;

      //閉じる・開く時の動作を振り分け変更したい時は変更してください
      let toggleT = 0.4;
      let toggleEase = "power4.out";

      if (!toggle.classList.contains('state-active')) {
        //アコーディオンが閉じているとき
        toggle.classList.add('state-active');
        toggleH = "auto";
        toggle.setAttribute("open", "true");
      } else {
        //アコーディオンが開いているとき
        toggle.classList.remove('state-active');
        toggleH = 0;
      }

      toggle.classList.add('state-animate');

      gsap.to(toggleCon, {
        height: toggleH,
        ease: toggleEase,
        duration: toggleT,
        onComplete: () => {
          //アコーディオンのアニメーション完了時
          if (toggleH == 0) toggle.removeAttribute("open");
          toggle.classList.remove('state-animate');
        }
      })
    }

    toggleBtn.addEventListener("click", toggleClick);

  }

}

document.addEventListener('DOMContentLoaded', function() {

  toggleFunction();

});

まとめ

デメリットもありますが動作的には上記のコードを流用すると問題なくなります。
今後は<details>と<summary>タグを使用するのがアクセシビリティ対策も簡単にでき、今後はメジャーになってくることが予想されますのでこの機会にまだ使用されていない方は是非一度お試しください。

ハメカメ ハメカメ

どうじゃ・・・
<details>と<summary>はこれから激アツじゃろう・・

Satoken サトケン

・・・

・・・

・・・

zzz。。。

・・・

説明が長いっす。。。

ハメカメ ハメカメ

・・・す すまん・・・

SHARE

  • Facebook
  • Twitter
  • はてブ
  • pocket
  • Line
Satoken
Satoken
フロントエンド関連をゴニョゴニョとマークアップする人
96万WEBパワーの持ち主。火事場のWEB力発動時7000万WEBパワーまで計測。
次の記事へ 納期前日でも安心! スマホのセレクトボックスはJavaScriptで書き出そう!
一覧へ戻るBack to List
  • にほんブログ村 IT技術ブログ Webサイト構築へ
  • ウェブデザイナーランキング
CONTACT
CONTACT キャラクター
CONTACT

KEPRATE ♥¥ ケプレイト ♥¥ CONTACT ♥¥ お問い合わせ ♥¥ KEPRATE ♥¥ ケプレイト ♥¥ CONTACT ♥¥ お問い合わせ ♥¥

  • TOP
  • BLOG
  • 48の必殺WEB
  • 【2023年最新版】HTMLのアコーディオンはdetailsやsummaryを使おう!
  • Home
  • About
  • Blog
  • Lab
    • KEP TYPING KEYBOARD
    • KEP SLOT
    • KEP DOT DRAW
  • Faq
  • Contact
    • Shop
    • Company
  • お知らせ
  • プライバシーポリシー
Tel :

050-5899-7800

Address :

〒573-0033
大阪府枚方市岡南町12-12

GoogleMap

KEPRATE
© 2014-2023 KEPRATE.