【Luxeritas】サイドバーにQiita風目次を設置

2019年7月27日Luxeritas

Install a Qiita-style table of contents in the sidebar

Qiita をご存知でしょうか。プログラム関係の検索をすると結構な確率で有用な情報が見つかります。そこに設置してある目次がめちゃ便利なんです。これを自分のブログにも設置したいとずっと思ってました。WordPressのブログへ引っ越したのを機にプログラム書いてみました。

どんなものか

Luxeritasはプラグインなしに記事の冒頭に自動で目次を挿入してくれるのだが、読み進んでスクロールしたら見えなくなってしまう。また、戻って目次を確認するなんて面倒です。

Qiitaの記事の目次はサイドバーに固定で常に表示されます。さらに、現在読んでいる目次項目がハイライトされて分かるようになっています。長い記事で目次項目が多いものを読むときはめちゃ便利です。

当サイトに設置したものの動作イメージは下記の動画で確認してください。

仕様

こちらで実装した固定目次の仕様は下記になります。

  1. サイドバーに目次を設置。
  2. スクロールに追従して、画面上部に固定表示する。
  3. 現在位置の目次項目をハイライト表示する。
  4. 画面の高さより大きいときは、高さを制限してスクロールバーを表示する。
  5. 現在位置の目次が表示領域から出ないように自動スクロールする。

Qiitaとは少し仕様が違います。Qiitaの場合は上位階層の目次項目もハイライト表示しますが、こちらのはそれはしてません。コードが複雑になるし、それほど必要性は感じなかったので、1項目のみのハイライトにしました。

5. の表示領域から出ないように自動スクロールする機能はQiitaのには実装されていないが、これはあった方が便利なので実装しました。

記事内に目次を自動で表示する

ダッシュボードから Luxeritas→カスタマイズ(外観)→目次 で設定します。自動で目次を設定する にチェックを入れると記事内に目次が自動で挿入されます。

他にもいろいろ設定項目がありますので、お好みで設定してください。

開始状態 の設定で 開いた状態閉じた状態 の選択ができるので、閉じた状態を設定しておくといいでしょう。サイドバーに常時目次か表示されるようになりますので、開いた状態にしておく必要はないと思います。

ただし、スマホではサイドバーの目次は表示されないので、その場合は必要に応じて開けられるようにしておきます。

サイドバーに固定表示する

ダッシュボードから Luxeritas→カスタマイズ(外観)→ウィジェット へ移動します。
スクロール追従サイドバー(H4タイプ)をクリックします。
ウィジェットの追加 をクリックします。
そこで 目次(Luxeritasオリジナル) をクリックします。

これだけでサイドバーに目次が固定表示されます。 上記の仕様の1.と2. は簡単に実現できました。Luxeritas、なんと素敵なテーマでしょう。

目次デザインのCSS設定

ダッシュボードから Luxeritas→カスタマイズ(外観)→追加CSS または 子テーマの編集→ style.css に下記のCSSを追加します。

/*スクロール追従バー(目次の設定)*/
/*表示設定*/
@media print, (max-width: 991px) {
    #side-scroll .toc_widget {
        display: none;
    }
}
/*各種設定*/
.toc_widget {
    position: relative;
    padding: 10px;
    border: 1px solid #ddd;
    color: #333;
    background: #fff;
}
/*タイトルの追加*/
.toc_widget:before {
    content: '目次';
    font-size: 1.2em;
    position: absolute;
    left: 17px;
}
.toc_widget>ul.toc_list {
    margin: 25px 0;
    overflow-y: auto;
}
/* サイド 目次 追従 カレント */
#side-scroll li a {
    display: block;
}
#side-scroll li a:hover {
    background-color: #e6f2ff
}
#side-scroll li a.current {
    background-color: #e6f2ff !important
}

やっていること

  • 1カラム(画面幅991px以下)では非表示
  • 背景色、境界線色などの設定
  • タイトル「目次」を疑似要素(:before)で挿入
  • 現在位置の目次項目の背景色の設定(次の Java Script でクラス追加する)

現在位置をハイライト、画面高に合わせてサイズ制限、リストの自動スクロール

JavaScript(jQuery)で、仕様の3.~5.を実現します。

Luxeritas→子テーマの編集→Javascript に下記のコードを追加します。

$(function() {
	// ナビゲーションのリンクを指定
	var navLink = $('#side-scroll 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] = $('div.meta-box');
	
	// 現在地をチェックする
	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_widget>ul.toc_list');
                var posTop = navLink.eq(i).position().top;
                if (tocList.innerHeight() < posTop + 60) {
                     tocList.scrollTop(tocList.scrollTop()- tocList.innerHeight() + posTop + 60);
                }
                if (posTop < 60) {
                    tocList.scrollTop(tocList.scrollTop() - 60);
               }

                break;
			}
		};
	}
 

	// ページ読み込み時とスクロール時に、現在地をチェックする
	$(window).on('load scroll', function() {
		currentCheck();
	});
	// ページ読み込み時とリサイズ時に目次のMaxHeightを設定
	var timer = false;
	$(window).on("load resize", function() {
    		if (timer !== false) {
        		clearTimeout(timer);
    		}
   		 timer = setTimeout(function() {
			$('.toc_widget>ul.toc_list').css('max-height', $(window).height() - 100);
    		}, 100);
	});
	
});

まとめ

サイドバーに固定目次を設置するのは Luxeritas の設定だけで簡単に実現できました。

現在位置の目次のハイライト等の実装は苦労しました。なんとか希望のものになりました。

コードが分からない方でもコピペするだけで実装できると思いますので、ぜひお試しください。