サイドバーに配置した「目次」ウィジェットをハイライト表示させる方法

Cocoonカスタマイズ集
この記事は約11分で読めます。
記事内に広告が含まれています。

記事に記載したコードは、スキンに追加されています。

はじめに

「目次」ウィジェットを「サイドバースクロール追従」に配置し、現在表示されている見出しの位置に対応する目次項目をハイライト表示させます。

この記事では、この方法について説明します。

完成イメージ

実装方法

以下に手順で実装します。

  • ラベル
    Javascriptを追加

    以下のコードをjavascript.jsに追加します。

    jQuery(function ($) {
      // サイドバー内にある目次ウィジェットのすべてのリンクを取得
      const $tocs = $('.sidebar-scroll .widget_toc a');
    
      // スクロール位置の判定時に用いるオフセット値
      const offset = 50;
    
      // 本文中の各見出しの識別子(ID)と位置座標を格納するための配列
      let headerPositions = [];
    
      /**
       * 文中の各見出しの位置(上からの座標)を計算して記憶
       *
       * @return {void}
       */
      function cachePositions() {
        headerPositions = $('.entry-content :header span').map(function () {
          const $el = $(this);
          return {
            id: $el.attr('id'),
            top: $el.offset().top - offset
          };
        }).get();
      }
    
      /**
       * 現在のスクロール位置に対応する目次リンクの表示を更新
       *
       * @return {void}
       */
      function highlight() {
        const scrollTop = $(window).scrollTop();
        let currentId = '';
    
        // 上から順に見出しの位置をチェックし、現在のスクロール位置がどの見出しを通過しているかを判定
        for (let i = 0; i < headerPositions.length; i++) {
          if (scrollTop >= headerPositions[i].top) {
            currentId = headerPositions[i].id;
          } else {
            break;
          }
        }
    
        // 該当する見出しのIDが見つかった場合、既存のハイライトを外して対象の目次リンクに .currentを付与
        if (currentId) {
          $tocs.removeClass('current');
          $tocs.filter(`[href="#${currentId}"]`).addClass('current');
        }
      }
    
      // 目次リンクが存在する場合
      if ($tocs.length) {
        cachePositions();
        highlight();
    
        // ページスクロールの場合
        $(window).on('scroll', () => {
          requestAnimationFrame(highlight);
        });
    
        // ページリサイズの場合
        let resizeTimer;
        $(window).on('resize', () => {
          clearTimeout(resizeTimer);
          resizeTimer = setTimeout(() => {
            cachePositions();
            highlight();
          }, 200);
        });
    
        // 「アコーディオン」ブロックなどの開閉による.main要素のサイズ変化を監視し、変化時に位置を再計算
        const mainElement = document.querySelector('.main');
        if (mainElement) {
          const resizeObserver = new ResizeObserver(() => {
            clearTimeout(resizeTimer);
            resizeTimer = setTimeout(() => {
              cachePositions();
              highlight();
            }, 100);
          });
          resizeObserver.observe(mainElement);
        }
      }
    });
  • ラベル
    CSSを追加

    以下のコードをstyle.cssに追加する。

    .sidebar-scroll .toc-list a.current {
      background-color: #ff0000;
    }
タイトルとURLをコピーしました