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

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

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

参考:IntersectionObserverを用いる方法

IntersectionObserverを使用して目次をハイライトする方法について説明します。
前述のscrollを使う方法との比較を以下に示します(表1参照)。

実現方法
方法メリットデメリット
scrollを用いる

  • 目次と次の目次の間を範囲としてハイライトが維持される。

  • スクロール方向に関係なくハイライトが切り替わる。
  • スクロール位置を常に監視するため、パフォーマンスに影響を与える可能性がある。

  • スクロールイベントの処理が重くなる。
IntersectionObserverを用いる
  • 画面に見出しが入った時点でハイライトされるため、パフォーマンスが軽い。


  • スクロールイベントに依存せず、効率的な処理が可能。
  • 下から上にスクロールした場合、以前の目次のハイライトが解除されないことがある。

  • rootMarginやthresholdの設定を調整しないと動作に問題が生じることがある。

実装手順

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

jQuery(function ($) {
  // サイドバー内にある目次ウィジェットのすべてのリンクを取得
  const $tocLinks = $('.sidebar-scroll .widget_toc a');

  // 本文中の各見出し要素(<span>)を取得して通常の配列に変換
  const headers = $('.entry-content :header span').toArray();

  // 要素の状態を監視
  const observer = new IntersectionObserver((entries) => {
    entries.forEach((entry) => {
      // 要素が画面内に入った場合
      if (entry.isIntersecting) {
        const id = entry.target.id;
        setActive(id);
      }
    });
  }, {
    // 判定基準となる交差領域の余白(画面上部10%を避け、上から10%〜20%のエリアに達した時を発火ポイントとする)
    rootMargin: '-10% 0px -80% 0px',
    threshold: 0 // 要素が20%見えたタイミングで発火
  });

  /**
   * 指定されたIDに一致する目次リンクに.currentクラスを付与しそれ以外から削除
   *
   * @param {string} id - 対象の見出しID
   * @return {void}
   */
  function setActive(id) {
    if (!id) return;
    $tocLinks.removeClass('current');
    $tocLinks.filter(`[href="#${CSS.escape(id)}"]`).addClass('current');
  }

  // 全見出しに対して監視を開始
  headers.forEach(header => observer.observe(header));
});
タイトルとURLをコピーしました