jQuery 簡易的なTOC(table of contents)を作ってみる

TOCとは、いわゆる見出しの目次です。多くのものは、その見出しをクリックすることで目的の見出しをすぐに表示できるようなものになっています。

実装的にはそんなに難しいものではないのですが、難しい事をしようとすると途端にややこしい処理が必要になったりします。今回は簡易的にTOCを目的の場所に入れるにはどうするかを解説してみようと思います。

サンプル

まずはcodepenで実装したサンプルを見てもらおうと思うのですが、できればPCで大きな画面で見てもらえるほうが助かります。

See the Pen LbPmej by Hidekichi (@Hidekichi) on CodePen.

full page view(ソース画面のないモード)

このサンプルでは、従来の2カラムのwordpressで言う所のサイドバーにはTOCを入れずにbodyの先頭に.stocを作り、それらをcssで右上に位置するように設定してあります。よって、1カラムのデザインなら比較的邪魔になりませんが、2カラム、3カラム等のデザインでは邪魔になるため、少し手を入れる必要があります。

スクリプトサンプル

※ 最適化や高速化はしていないので必要であれば手を加えてください

※ scssで不要なものも書いてますがとりあえずのスタイルです

動作説明

実質的に必要なものは、

  1. 各見出しに専用IDを付ける
  2. jQueryにて、ターゲットのクラスに見出しの中身と仕組みを設置する

これだけです。
1)については、#the-content(記事部分)の中にある見出しを探して、そこに連番でIDを入れます。(2)と同時にやってますがこれら処理をしながら同じ値を利用してTOCのリンク部分等も作っています。リンク部分言ってもaタグではありません。

2)については、コメント「TOCを挿入する場所」にて、見出しのテキストを入れるブロックを作り、テキストをまずdivで囲んでいきます。

よくあるものはul等でリストを作ってそのインデントを利用するというのをよく見ますが、今回は全てdivで囲んで別途クラスを付与することでインデントを作っています。

スクリプトの肝は、

ここだろうと思います。

まず#the-content内のh1〜h6を調べて、それらをそれぞれにeachで繰り返し処理します。
textで各見出しのテキストを取得して、elemで要素名を取得しています。要素名はH1であったりH3であったりです。この「h」部分を取り除けばTOCの中のインデントをどれだけ取ればよいのかを設定できます。
具体的な方法としては、scssの.g-1や.g-3などです。見出しは6つしか無いので、それぞれにどれだけインデントを付けるかを設定さえしておけば、それに対応するようにclassをつければよいだけなのです。

サンプルの設定ではh1とh2はインデントなし。それ以外は1em(1文字分)ずつ右にインデントするようにしています。よって、

このような感じになります。これで見出しレベル毎にインデントをつけられるようになります。インデントが不要なのであれば単純にmargin-left自体を取り除いて、色を変えるなり他の方法でスタイルしても良い部分です。

この処理部分では、変数headersに各見出しを調査して入れてある状態です。そのためこれらをeachでループ処理すれば各見出しにIDを付けることもできます。

この$(this)は取得した#the-content内の各見出しが順番に入っていきます。

記事内に、

このようなhtmlがある場合、headersには、h2(テスト見出し2)、h3(テスト見出し3-1)、h3(テスト見出し3-2)と入っていることになります。この$(this)はこれらが順番に入っていきます。なので、ここは各見出しのIDにstoc(i)をセットしますということになります。よって、

こんな感じになるということです。stoc(i)のiは変数headersのindexです。0から順番にインクリメントされます。これはeachのfunction(i)が動作しています。

の部分は、TOCの中身です。各見出しをdivで囲んで、かつリンクさせるためにIDではなくclassを設置します。

このようなhtmlがTOCの中に入ることになります。.inHは識別用、.stoc(i)はターゲット(見出し)取得用、.g-見出しレベルは見た目のスタイル用のクラスです。

.stoc(i)は各見出しの#stoc(i)に対応しています。同じ処理から作ったので同じものが入るということです。違うのはclassであるかIDであるかです。ターゲットになる見出しは重複しませんからIDでよく、TOCの中身もIDでも良いわけですが、同じページ内にIDが複数あるとマズイ(ターゲットの見出しのオフセットを調べたりする場合、htmlの作法的に等の理由で)のでclassにしてあります。

これらclassは

この部分で使われます。
「.stocの中にある.inHをクリックした時」と言うのが条件で、stocIdでは、

このclassを全部取得します。例えば見出しレベルが3の場合(h3が対象の場合)、クラスは「inH stoc(i) g-3」と言うクラス名が取得できます。それをsplitでスペースを対象に分けます。すると、

このように、なるので必要なstoc(i)は[1]番目のものだとわかります。このstocIdはscrollPos()関数に渡されてオフセットを調べるのに利用されます。

scrollPos()関数では、まず、その引数(classes)が「pageTop」以外の場合に、先頭に#をつけます。stoc3と言うのがclassesとして渡された場合、pageTopではないので#stoc3というふうになるという具合です。
wordpressにログインしていない場合はadminbarの影響を受けないので自分がログインしている時に対象がスクロールしてアドミンバーに隠れたとしても良い時は、アドミンバーの処理は必要ないので、

これだけわかればよいです。これはつまり、記事中の見出しのオフセットです。ページ内のスクロールは調べたオフセットの位置にスクロールさせているだけです。

もしスクロールせずにパッと移動させる場合はanimateではなくcssでtop位置を変えてもよいかと思います。他にもTOCの中身をdiv以外にaタグを使ってページ内リンクにしても良いです。

このような感じで自前でTOCを導入することができます。

応用編

例えば、スクロール追従部分に入れてもよいかと思います。その場合は、テキストウィジェットをスクロール追従部分に配置して、

これをテキストウィジェットの中に入れます。

jQueryから、

この部分をコメントアウトなりして機能させないようにします。

サイドバーに入れられるということは記事の先頭にも入れられるということですが、記事の先頭はすぐに隠れてしまうためにあまりメニューとしては優秀ではないかも知れません。ここらは使い方次第です。

スクロール追従に入れた場合のサンプル 2016/11/18追加

See the Pen TOCをスクロール追従に入れるサンプル by Hidekichi (@Hidekichi) on CodePen.

ここでは、サイドバーが切れると思うので実際のサンプルをcodepenで、かつレスポンシブ対応させてないので画面右上のchange viewより一番下にあるeditor viewを真ん中のソース部分が上にくるようにしてもらうと全体がわかりやすいかと思います。

あるいは、fullpage viewで見てみてください。
応用編で説明したよりももう少し手を入れる必要があって、css部分も調整したものをサンプルでは掲載しています。
ここまでサンプルで作っておけば後はデザインの問題となるかと思います。

スポンサーリンク

シェアする

フォローする