[jQuery]スクロールに連動して目次をアクティブにする

ユーザーに対して表示中のコンテンツを見える化する

サイトの構成には様々あり、中には目次を常に表示しているサイトも存在します。
目次を常に表示する意図としてはコンテンツが多岐に渡るためガイドを目的として設置されていたり、目的の内容へジャンプするためのリンクであったりと、どちらもユーザーに配慮したものとなっています。

更に欲を言えばどの目次の内容を表示しているかひと目で分かることができれば助かりますよね。

そこで次の機能を実装したいと思います。

See the Pen scroll-point by nanase webdesigner (@nanase-777) on CodePen.

コンテンツの用意

高さの異なるボックス(box1~4)を用意します。.js-boxというクラスはjQueryで制御するためのクラスということで頭に「js」を付けて区別しています。

文字色とフォントサイズを変更したい目次に対しては.activeというcssを追加します。

目次のol要素をposition:fixed;で固定表示しています。中身のli要素はjQuery側で追加するので空白です。

ボックス数に合わせてli要素を作成

目次を手作業でコーディングする方法もありますが、管理しやすくするためにボックスの内容を元に自動で作成されるようにしたいと思います。

「ボックスの見出し」=「目次の見出し」にするため、text()でボックスの見出しのテキストを抜き出します。
そしてテキストをli要素に収め、append()で親要素であるol要素へ追加していきます。

コンテンツが表示中か否かの判定

ボックスがウィンドウに表示された場合は対応する目次のli要素に.activeを追加し、表示されなくなった場合は.activeを取り除きます。

.activeを取り除く条件ですが、「スクロールがウィンドウ上部へ通り過ぎた場合」「次のボックスが表示された場合」なので各タイミングで.activeを取り除きます。

どのli要素へ.activeを追加するかですが、まずボックスが何番目の要素かを$(this).index()でカウントします。そしてカウントした順番をli要素に対してeq()で指定することで対応可能です。

※厳密には画面半分の位置(windowHeight/2)を境に判定しています。

次の要素のY座標について

最後尾のボックスは次のボックスの座標を取得できません。そこで代わりとしてサイト全体の高さ($(document).height())を渡すことで「ウインドウ内である」という判定にします。

最後尾か否かはeach(function(i){})のインデックスiを利用します。インデックスがボックスの数と一致すれば最後尾となります。注意点としてインデックスは0からカウントするので、事前に+1しておきます。

以上でスクロールに連動して目次をアクティブにする機能が実装できました。