WordPress サイドバー追従目次のハイライト方法

jsで作ります。WordPressのCocoonのtecurio skyの例です。 違うテーマの場合は要素、クラス名、id名が異なると思うので、Chromeの右クリック『検証』で確認してくださいね。 私の場合、サイドバーの目次での表示はh3までとしているので、以下のプログラムでの対象はh2とh3です。

プログラムは簡単です。 スクロールUp時とDown時でハイライトのタイミングを調節しているので少しプログラムが長くなっていますが、Up, Down判別が無ければめっちゃ簡単です。

Javascriptのクラスや正規表現を始めて使った初心者です。もっと良い方法があれば是非コメント頂ければ嬉しいです。

手順

  1. 目次に付いたid用に空の配列this.idsを用意する。
  2. spanタグのidに”toc”から始まる要素数をthis.lenに入れる。
  3. this.start_pos(スタート位置)を定義(スクロールUp、Downの判断に使用)
  4. this.vp_height(画面サイズ)を取得(ハイライトするタイミング判定に使います)
  5. this.indexを定義。このthis.index番号の要素にcss色付けします
  6. this.ids[ ]に’toc1′,’toc2′,…と要素数(this.len)分を作ります。私の場合(Cocoon tecurio sky)、目次も本文のヘッダ(h2,h3)もidに’toc1’から順番に数字が付いています。 本当はh2とh3のidを直接this.ids[ ]に配列で取り込みたかったのですが、やり方が分からなかったので、h2,h3のidと同じになるように’toc’+数字をthis.ids[ ]に入れて言っています。
  7. ここまでをvar sb = new SideBarTOC();が実行された時に準備されます。
  8. ここから下は、$(window).on(‘load scroll’…で画面がscrollされた時のイベントで、上で準備した情報を使い、ここから下の処理を実行しています。
  9. scrollイベント発生時、Up、Downを判断して
  10. forループで最大this.len分(全ての)’toc’の位置を取得して、縦座標がマイナスとプラスになる連続する2つの’toc’があった場合に、
  11. スクロールupの場合とdownの場合で、その2つの’toc’位置に応じてcssを書き換えています。
  12. 書換は、まず全ての”toc”を”Transparent”で色を無くして、ハイライトしたい”toc”だけ色を設定しています。
  13. 私のサンプルはサイドバー以外に、本文のh2,h3の文字も”text-shadow”で少し目立つようにしているので、2行追加しています。

実際のプログラム

javascript.jpの最後に追加するだけで動作します。 私は自分のjsファイルに入れ、自分のプラグインphpの中でwp_enqueue_script()を使って読み込んでいます。(実は簡単で、他に悪影響もないので便利です。 方法は今後Upしていきます。)

// class SideBarTOC{ とすると動作はするが、"SideBarTOC has already been declared"のエラーがでる。
// 以下の書き方ならエラーが出ない。
var SideBarTOC = class{
	constructor(){
		this.ids=[];
		this.len=document.querySelectorAll(`span[id^='toc']`).length;
		this.start_pos=0;
		this.vp_height = window.innerHeight;
		this.index = 1;
		for(let i = 1;i <= this.len; i++){
			this.ids[i] = "toc"+i;
		}
	}
	check_direction(){
		let current_pos = document.getElementById("container").getBoundingClientRect().top;
		if(current_pos > this.start_pos){
			return 'up';
		}
		if(current_pos < this.start_pos){
			return 'down';
		}
		this.start_pos = current_pos;
		return 'none';
	}
	
	index_highlight(){
		let toc_pos = [];
		let ratio;
		let direction = this.check_direction();
                //console.log(direction); //debug用
		for(let i = 1; i <= this.len; i++) {
			toc_pos[i] = document.querySelector(`span[id="${this.ids[i]}"]`).getBoundingClientRect().top;
			if(i >= 2){
				if(toc_pos[i-1] < 0 && toc_pos[i] > 0){
					ratio =(toc_pos[i]-toc_pos[i-1])*0.6;
					if(direction=="down"){
						if(toc_pos[i]-ratio < 0){
							if(toc_pos[i] < this.vp_height*0.95){
								this.index = i;
							}
						}
					}else{
						if(toc_pos[i]-ratio > 0 || toc_pos[i] > this.vp_height){
								this.index = i-1;
						}
					}
					break
				}else{
					if(toc_pos[1] > 0){
						this.index = 1;
					}
					if(toc_pos[this.len]<0){
						this.index = this.len;
					}
				}
			}
		}
		$(`#sidebar a[href^="#toc"]`).css('background-color', 'Transparent');
		$(`#sidebar a[href="#${this.ids[this.index]}"]`).css('background-color', 'whitesmoke');
		$(`.main span[id^="toc"]`).css('text-shadow', 'none');
		$(`.main span[id="${this.ids[this.index]}"]`).css('text-shadow', '0 0 0.5rem LightSkyBlue');
//text-shadowでなくボックス全ての色を変えて目立たせたい時は下を使います。
// 		$(`.main  h2:has(span[id^="toc"]), .main  h3:has(span[id^="toc"]) `).css('background-color', 'Transparent');
// 		$(`.main h2:has(span[id="${ids[index]}"]), .main h3:has(span[id="${ids[index]}"])`).css('background-color', 'Azure');
	}
}

$(function(){
   //引数は人によって異なるかも
   //サイドバー目次の属性をF12で確認してください
   var sb = new SideBarTOC();
    $(window).on('load scroll', function() {
		sb.index_highlight();
    });
});

プラグインにjsを組込む方法

コメント