標準のdetailsブロック(詳細ブロック)をスムーズに開閉させる方法(メモ)

公開日:2024(令和6)年3月23日/最終更新日:

,
WordPressのカスタマイズ方法についての画像



WordPressの「詳細ブロック」ご存じですか?数々のブロックの中にひっそりとあるので気づいてすらいない方も多いのでは?

このブロックを使うと、タイトルをクリックしたら中身がバーンと展開される、開閉式のコンテンツ(いわゆるアコーディオンコンテンツ)が簡単に作れるんです。しかもいくつものブロックを自由に内包でき、色なども指定できるので、今までショートコードや特別なブロックを持つプラグイン・テーマで実装していたものが簡単に実現できます。

ただ、この「詳細ブロック」にはちょっと難点というかアレな部分があります。それが、開閉がガクッと進むことです。クリックしたらゆっくり開いて、もう一度クリックしたらゆっくり閉じるなんてことができないんです。

まあそれでも開閉コンテンツのためにプラグインを使うよりはいいのかもしれないですけど、できればゆっくりを実現させたい!と思って試行錯誤したら実装できましたので、メモを兼ねて公開しておきます。

多分このページに到達した方は同じことを考えていて、挫折した方かも..と感じますので、詳しい説明は後にして、さっそく以下で動作サンプルをお見せします。

「詳細」ブロックのタイトルというところをクリックすると中に入れた複数のブロックで作ったコンテンツがゆっくり開閉します)

「詳細」ブロックのタイトル

段落を挿入

列1列2列3
表の内容表の内容表の内容
表の内容表の内容表の内容
表の内容表の内容表の内容
標準のdetailsブロック(詳細ブロック)をスムーズに開閉させる方法(メモ)|Personal WP Customization Notes (PWCN)

どうでしょう?ああいい感じ..と感じた方は実装してみてくださいね。

自身この方法でサイトへ実装し続けようと思っているので、時点ではうまく動作しているかと思いますが、ひょっとして未来にもっといい方法があったら変更しているかも知れませんし、動作を停止させているかも知れませんのであしからず..

「詳細」ブロックをゆっくり開閉させるためのコード

jsファイルを作って以下のコードを挿入し、WordPress標準のjQueryの後で読み込まれるようにすれば動作します(こんなカスタマイズする人はこの程度の解説で実装可能でしょう..)。

/*** 標準のdetails(詳細)ブロックをゆっくり開閉 ***/
/* /summaryタグの後ろから/detailsタグの手前までをdivでラップ */
document.addEventListener("DOMContentLoaded", function() {
  // summary要素を取得
  var summaries = document.querySelectorAll("summary");

  // 各summary要素に対して処理を行う
  summaries.forEach(function(summary) {
    // details要素を取得
    var details = summary.parentNode;

    // details要素内のsummary要素とそれに続く兄弟要素を取得
    var elementsToWrap = [];
    var currentElement = summary.nextElementSibling;
    while (currentElement && currentElement.tagName !== "DETAILS") {
      elementsToWrap.push(currentElement);
      currentElement = currentElement.nextElementSibling;
    }

    // 要素をdivでラップする
    var wrapper = document.createElement("div");
    elementsToWrap.forEach(function(element) {
      wrapper.appendChild(element);
    });

    // div要素にクラスを付与
    wrapper.classList.add("pwcn-accordion__content"); // ここにクラス名を指定してください

    // div要素をdetails要素内の正しい位置に挿入
    details.insertBefore(wrapper, summary.nextSibling);
  });
});

/* details summaryタグにそれぞれクラスを付与 */
jQuery(function () {
  jQuery('details').addClass('pwcn-accordion');
  jQuery('summary').addClass('pwcn-accordion__title');
});

/* クリックでゆっくり開閉 */
jQuery(function($) {
    let accordionDetails = '.pwcn-accordion';
    let accordionSummary = '.pwcn-accordion__title';
    let accordionContent = '.pwcn-accordion__content';
    let speed = 400;

    $(accordionSummary).each(function (){
        $(this).on("click",function (event){
            event.preventDefault();
            let content = $(this).nextAll(accordionContent);
            if($(this).parent(accordionDetails).attr("open")){
                content.css('max-height', content.height()).animate({ 'max-height': 0 }, speed, function(){
                    $(this).parent(accordionDetails).removeAttr("open");
                    $(this).hide().css('max-height', '');
                });
            }else{
                $(this).parent(accordionDetails).attr("open","true");
                content.css('max-height', 0).show().animate({ 'max-height': content.prop('scrollHeight') }, speed, function(){
                    $(this).css('max-height', '');
                });
            }
        })
    });
});

コードで行っていること

ブロックを使わず、detailsタグでjQueryを使ってスムーズに開閉させる場合、以下のようなhtml構造を作って、開閉に応じてjQueryで処理させるというのが一般的です。

<details class="全体をラップするクラス">
  <summary class="タイトルのクラス">Title</summary>
  <div class="開閉させる内容のクラス">
  .....
  </div>
</details>

jQueryは割愛しますけど、動作的にはこんな感じになりますね。

  • summaryタグの内容を表示させておく
  • 「タイトルのクラス」が付与されたsummaryタグをクリックしたらdetailsタグに「open」属性を付与する
  • open属性が付与されている間だけdivタグの中身を表示

これを実現しようとすると、WordPress標準のdetailsブロックでは以下の措置が必要になります。

  1. detailsタグに「全体をラップするクラス」を追加する
  2. summaryタグに「タイトルのクラス」を追加する
  3. 複数あるかもしれない中身全部を「開閉させる内容のクラス」を持つdivタグで囲む

先ほどの全コードの中で1と2を行っているのが以下の部分、これはaddClassで簡単に追加できます。

/* details summaryタグにそれぞれクラスを付与 */
jQuery(function () {
  jQuery('details').addClass('pwcn-accordion');
  jQuery('summary').addClass('pwcn-accordion__title');
});

苦労したのが、開閉させる中身をdivでラップする部分。まあブロックエディター内で中身すべてを手動でグループ化して「開閉させる内容のクラス」を付与すればいい話ではあるものの、できれば自動化したい、そこで出来上がったのが以下の部分、出力されたHTMLから要素を探して必要なところをdivで囲むという措置です。

/* /summaryタグの後ろから/detailsタグの手前までをdivでラップ */
document.addEventListener("DOMContentLoaded", function() {
  // summary要素を取得
  var summaries = document.querySelectorAll("summary");

  // 各summary要素に対して処理を行う
  summaries.forEach(function(summary) {
    // details要素を取得
    var details = summary.parentNode;

    // details要素内のsummary要素とそれに続く兄弟要素を取得
    var elementsToWrap = [];
    var currentElement = summary.nextElementSibling;
    while (currentElement && currentElement.tagName !== "DETAILS") {
      elementsToWrap.push(currentElement);
      currentElement = currentElement.nextElementSibling;
    }

    // 要素をdivでラップする
    var wrapper = document.createElement("div");
    elementsToWrap.forEach(function(element) {
      wrapper.appendChild(element);
    });

    // div要素にクラスを付与
    wrapper.classList.add("pwcn-accordion__content"); // ここにクラス名を指定してください

    // div要素をdetails要素内の正しい位置に挿入
    details.insertBefore(wrapper, summary.nextSibling);
  });
});

最後に、開閉速度をもう少し速く(遅く)したい場合は以下の部分の400という数字を変更してください。

    let speed = 400;

後述..実はこのコード、自前で全部作成したものではなく、インターネット上で紹介されていた多数のditailsタグに対するアニメーション化の情報をいくつも実装してコードの理解をある程度した上で、AI様と対話して作成したものです。長年やりたくてもできなかったことがこうして実現できると、AIのありがたみを感じますね。

多分問題はないと思うけど...

このコードをそのまま実装すると、詳細ブロック(detailsタグで囲まれた要素)に対してすべて同じことをするので、要はサイト内すべての詳細ブロックがこの仕様になります。

コードでは、クラスの追加と、中身をdivで囲むということしかしていないので、追加するCSSクラスが被らなければ、多分他のプラグインなどが出力するものと競合したりすることはないとは思いますが、不具合があれば以下のような対処は必要かも知れません。

  • jQuery(上記のコード)を有効にするかどうかのスイッチを投稿ごとに判断する等適所に設ける
  • ブロックスタイルを作って、スムース開閉させるかどうかを切り替えにする(全体をラップする方法を変える)

ついでに△マークを変更する方法も

機構とは全く関係ありませんが、本当についでとしてボタン先頭に出る「▼」マークの変更方法をメモしておきます。

以下のようにすると、after要素(開閉文字列の後ろ)に「【▽ クリックして開く】」「【△ クリックして閉じる】」と表示させるよう変更できます。

「content」要素をサイトのCSSコード上で編集をすると、うまく適用されないことがあるので、デベロッパーツール上で変更して貼り付けるようにすると文字変更もうまくいくことが多いですヨ

details>summary {
  list-style-type: none;
  outline: none;
  cursor: pointer;
}

details>summary::-webkit-details-marker {
  display: none;
}

details>summary::after {
    content: ' 【▽ クリックして開く】';
}

details[open]>summary::after {
    content: ' 【△ クリックして閉じる】';
}

details[open]>summary {
  margin-bottom: 0.5rem;
}