Blog

【Cocoon】サイドバーの目次を記事の位置にあわせて追従させる

はじめに

Woredpressに移行したということもあり、以前よりやってみたかったQiitaやZennのような記事の位置に合わせてサイドバーの目次をハイライトおよび追従させるといったカスタマイズをやってみる

完成イメージ

  • 初期状態として目次の一つ目をハイライトする
  • 記事のスクロールに併せて目次のハイライト位置が移動する(進むも戻るも対応)
  • 目次が長すぎる場合には目次にスクロールバーを表示して、自動スクロールを行う

テーマ

Cocoon

手順

サイドスクロール追従領域に目次ウィジェットを配置

記事をスクロールしてもサイドバーのウィジェットを固定で表示してくれる機能があるので、まずそこにウィジェットを配置する。

  1. [Wordpress管理画面] > [外観] > [ウィジェット]に遷移する
  2. 利用できるウィジェット内の**[C]目次サイドバースクロール追従**に追加する
  3. 必要に応じて目次表示の深さを変更する

これで記事をどれだけスクロールしてもサイドバーに目次が表示されている状態になる。

あとは、記事の位置に合わせて目次をハイライトしていく。

JavaScriptの修正

  1. [Wordpress管理画面] > [外観] > [テーマファイルエディター] > [外観]に遷移する
  2. 右側メニューのjavascript.jsをクリックする。
  3. 左側のテキストボックスに以下を張り付ける。

以下はこちらの記事をベースにCocoonに併せてカスタマイズ+機能ちょい足ししています。

$(function() {
	// ナビゲーションのリンクを指定
	var navLink = $('#toc-2 .toc-list li a');
	if(!navLink[0]) {
		return false;
	};

	// 見出しを配列に格納
	var contentsArr = new Array();
	for (var i = 0; i < navLink.length; i++) {
		// 見出しを取得
		var targetContents = $(navLink.eq(i).attr('href'));
		// 配列に格納
		contentsArr[i] = targetContents

	};
	//投稿記事エリアの次の要素を最終位置取得用に追加
	contentsArr[i] = $('footer.entry-footer');

	// 現在地をチェックする
	function currentCheck() {
		navLink.removeClass('current');
		// 現在のスクロール位置を取得
		var windowScrolltop = $(window).scrollTop() + 50;

		for (var i = 0; i < contentsArr.length-1; i++) {
			// 現在のスクロール位置が、見出しと次の見出しの間にあるものを調べる
			if(contentsArr[i].offset().top <= windowScrolltop && contentsArr[i+1].offset().top > windowScrolltop) {
				//ナビゲーションにclass="current"をつける
                navLink.eq(i).addClass('current');

               //目次のカレントが表示領域から出たら見える位置までスクロール
                var tocList = $('.toc-content>ol.toc-list');
                var posTop = navLink.eq(i).position().top;

                var param = 150
                if (tocList.innerHeight() < posTop + param) {
					tocList.scrollTop(tocList.scrollTop() - tocList.innerHeight() + posTop + param);
                }
                if (posTop < param) {
                    tocList.scrollTop(tocList.scrollTop() - param);
				}

                break;
			}else if(contentsArr[0].offset().top >= windowScrolltop){
				//ナビゲーションにclass="current"をつける
                navLink.eq(0).addClass('current');
                break;
			}
		};
	}

	// ページ読み込み時とスクロール時に、現在地をチェックする
	$(window).on('load scroll', function() {
		currentCheck();
	});
});

ざっくりだけどやっていること

  1. スクロールごとに位置チェック(currentCheck)を実行する。
  2. 現在位置が記事のどの見出し位置にいるかをチェックする。
  3. 場所がわかったらサイドバーの目次をスクロール + currentクラス追加

cssの修正

  1. [Wordpress管理画面] > [外観] > [テーマファイルエディター] > [外観]に遷移する
  2. 右側メニューのスタイルシートをクリックする。
  3. 左側のテキストボックスに以下を追加する
/* toc====================== */
ol.toc-list {
    max-height: 500px;
    overflow-y: auto;
    width: 260px;
}

/* h1 */
.toc-list li a.current {
    background-color: #9fe19a !important;
}

/* h2 */
.toc-list ol > li:has(a.current)  {
    background-color: #9fe19a !important;
}

.toc-list li a:not(.current) {
    color: #c9c9c9
}


.toc-list li a:hover {
    background-color: #9fe19a;
    color: #208848;
}

おわりに

無事にQiitaっぽい感じの目次追従はさせることができました。

今後はZennっぽいもっといかしたcssを組み込めればなーと思っています。

参考