サイドバーに現在位置を表示して追尾する目次を設置する【Ver3】

いわゆるシングルページナビゲーションみたいなやつ(はてなブログ用) (2021/08/12 ver3.4.1)

できること

  • サイドバーに目次を設置
  • スクロールに合わせて追尾
  • 現在位置の背景色変更
  • リンク先へのスムーズスクロール
  • ウィンドウサイズ変更に合わせてサイズ変更
  • 記事ページ以外でもページ内の記事一覧などを表示


動作環境

  • サイドバーが横に表示されるテーマ
  • 親要素にtransformoverflow:hiddenがないテーマ

動作確認はPC版のChromeFireFox、Edge、IE11で行っています。
※設定によってはスマホタブレットでも動きますが基本的にPC向けです。

※追記(2021/08/12)
はてなブログの推奨ブラウザからIE11が削除されたため、本スクリプトもver3.4.1以降はIE11での動作確認はしません。

前バージョンからの主な変更点

  • ウィンドウサイズ変更に対応
  • 使えるテーマが増えた
  • HTML構造変更(<div class="sectionList"></div>削除など)
  • CSSのidとクラス名変更#sectionListSide → #stoc.current → .activeなど)

他にもjQuery使うのをやめたりと変更点が多いです。

その他の変更履歴

インストール

念のため追加する前の設定は保存しておきましょう。
テスト用の非公開ブログを作ってそっちで試すといいかも。

HTML

デザイン設定の[カスタマイズ]-[サイドバー]-[モジュールを追加]-[HTML]に貼り付ける。
タイトルはスクリプトが読み込まれた段階でスクリプト側で設定したものに置き換えられます。

JavaScript

デザイン設定の[カスタマイズ]-[フッタ]に追加する。
(上のHTMLの後に追記してもたぶん動きます)


これを開いて全てコピー → sidebar_toc_footer_minify(Raw)


見づらいですが圧縮前のもデバッグ用にどうぞ → sidebar_toc_footer(Raw)

CSS

デザイン設定の[カスタマイズ]-[デザインCSS]に追加する。

※追記(2019/04/01)
前バージョンのCSSを流用する場合、一部のidとクラス名が変更されたり書き方が変わっているので注意(#sectionListSide → #stoc.current → .activeなど)。

文字色(2018/04/22追記)

デフォルトではページに設定されたリンクの色が使われますが、変更したい場合はCSSに下記を追加します。
上のCSSコメントアウト/*~*/)されている部分を編集してもOK。

#stoc a {
  color: #カラーコード;
}
/* マウスカーソルをあわせた時 */
#stoc:not(.touch) a:hover {
  color: #カラーコード;
}
/* 現在位置 */
#stoc .active {
  color: #カラーコード;
}

/* 訪問済みのリンクの色(一色だけにしたい場合は指定しない ※テーマによる) */
#stoc a:visited {
  color: #カラーコード;
}

設定について

JavaScriptの冒頭部分を変更することである程度動作を変えられます。そのままでも動きますがテーマや好みに応じて変更してください。
trueはオン、falseはオフみたいな意味で、目次タイトル以外の部分は全て半角で書きます。

ここで説明するもの以外についてはこちら→ オプション

メディアクエリ(重要)

変数名 デフォルト
MATCH_MEDIA false
MEDIA_QUERY_SIDEBAR '(min-width: 768px)'

目次がスクロールに追尾してこない・現在位置の背景色が変更されないなどの場合はこの設定を見直してみてください。

デフォルトではサイドバー要素が横に表示されているかを#box2要素のfloatプロパティがnoneかどうかで判定しています。
(正確にはgetComputedStyle(document.getElementById('box2')).cssFloatの結果がnoneかどうか。floatがない場合もnoneが返される)

多くのテーマはそのままで動きますが、Boilerplateを元に作られたテーマなどはfloatのかわりにメディアクエリが使われていて判別が難しいので、直接サイドバーが表示されるページの横幅を設定します。

MATCH_MEDIAtrueにして、MEDIA_QUERY_SIDEBARにサイドバーが表示されるページ幅の条件などを書きます。
MEDIA_QUERY_SIDEBARにデフォルトで入ってる'(min-width: 768px)'だと「ページの横幅が768px以上」ならサイドバーが表示されている(追尾する)という意味になります。

条件の調べ方(2019/04/08追記)

ページの横幅がいくつならサイドバーが表示されるかはテーマによって違うので、以下の方法で調べた数値を〇〇の部分に入れます。

const MATCH_MEDIA         = true;
const MEDIA_QUERY_SIDEBAR = '(min-width: 〇〇px)';
例1:簡単な調べ方
  1. ブログの記事ページを開く
  2. F12を押すと出るデベロッパー(開発者)ツールを開く
  3. コンソール(Console)の入力欄(>>>の所)に下のコードを貼り付けて実行(Enter)
  4. ウィンドウ幅を調整してサイドバーが表示されるギリギリのサイズの時、コンソールに表示されている数値
window.addEventListener('resize', function(){console.log(window.innerWidth)})

Chromeデベロッパーツールを開くだけでもウィンドウ幅変更時に右上にサイズが表示されます)

例2:CSSを調べる方法

f:id:Twilyze:20190401053720p:plain
#box2にメディアクエリが指定されている場合)

  1. ブログの記事ページを開く
  2. ChromeなどのブラウザでF12を押すと出るデベロッパーツールを開く
  3. サイドバー要素(#box2)を選択
    body > #container > #container-inner > #content > #content-inner > #box2
  4. ウィンドウ幅をサイドバーが表示されない状態まで調整(必要以上に小さくしない)
  5. Stylesペインに④の@media ~があればmax-widthまたはmin-widthを確認
    今回の場合は'(max-width: 1138px)'なので「ページの横幅が1138px以下」で適用されるCSSが書いてある。
    つまりサイドバーが表示されるのは「ページの横幅が1139px以上」のはずなので〇〇には1139を入れる。

目次を表示するページ

記事ページでは普通に見出し一覧を表示しますが、トップページやアーカイブページでは記事タイトル一覧を目次として表示できます。

デフォルトでトップページやアーカイブページでも表示するように設定されていますが、必要無い場合はpage-indexpage-archivedisplaytrueからfalseに変更してください。

  {
    class    : 'page-index',
    title    : 'このページの記事一覧',
    listPage : true,
    display  : false  // ここをtrueからfalseに
  },
  {
    class    : 'page-archive',
    title    : 'このページの記事一覧',
    listPage : true,
    display  : false  // ここをtrueからfalseに
  },

設定を変えればタイトルの変更やPro機能の固定ページでも表示できます。
詳しくはこちら→ PAGES options

表示する見出しタグ

変数名 デフォルト
HEADLINE_QUERY ['h3', 'h4', 'h5']

目次に表示したい見出しタグを指定します。数字が小さい順に書いてください。

固定時の余白

変数名 デフォルト
MARGIN_TOP 10
MARGIN_BOTTOM 0

目次モジュールが固定(追尾)される時の上下の余白を指定できます。

グローバルメニュー

変数名 デフォルト
GLOBAL_HEADER ['#globalheader-container']
  • 追尾開始位置を正しく取得するために、ヘッダーメニュー・グローバルメニューなどと呼ばれる常時表示・追尾型のメニュー(上に表示されるもの)のidclassを指定します
  • idの場合は#classの場合は.を先頭に書いてください
  • classを指定して複数の要素が見つかった場合でも先頭の要素だけを対象にします
  • 複数指定する場合は['#globalheader-container', '.global-menu']のようにコンマで区切って書きます
  • Pro版でヘッダを非表示にしている場合はデフォルトの'globalheader-container'は消しても大丈夫ですが[]は残してください
  • 目次と重なる時はMARGIN_TOPを調整します

見出し位置などの更新(2018/05/03追記)

変数名 デフォルト
CLICK_UPDATE []
CLICK_UPDATE_DELAY 500

現在位置表示用の見出し位置や目次の追尾開始位置などはロードが終わった時やウィンドウサイズ変更後に取得していますが、他の要因で高さが変わるとずれてしまいます。
このオプションで指定された要素をクリックすると見出し位置などの更新が行われるので、クリックによる目次の開閉などで高さが変わってもずれないように出来ます。

  • CLICK_UPDATEに指定する要素の書き方は上のGLOBAL_HEADERと同じ
    ただしclassを指定した場合は見つかった全ての要素が対象になります。
  • CLICK_UPDATE_DELAYは要素クリック後の更新を遅らせる時間
    アニメーションの時間などを考慮して調整してください。

たとえば['.archive-module-hide-button', '.archive-module-show-button']と指定すると、サイドバーの「月別アーカイブ」モジュールの開閉ボタンをクリックした後に更新され、目次の追尾開始位置のずれを防げます。

スムーズスクロール

変数名 デフォルト
SCROLL_TIME 400
SCROLL_TIME_TOUCH 0
  • スムーズスクロールにかける時間をミリ秒で指定できます(TOUCHが付いてる方はタッチデバイスでのスクロール時間)
  • 0にするとスムーズスクロールはオフになります

HTML構造

色が薄い所ははてなブログ側で用意される部分です。
nav#stocはインストール時に貼り付けたHTML)

f:id:Twilyze:20180301042610p:plain

追尾中#stoc-moduleに付与されるクラスの条件

  • tracking ウィンドウかページに固定している時
    • fixed ウィンドウに固定している時
    • absolute ページに固定(下までスクロール)している時
  • fade-in 他のサイドバーモジュールより上に設置していて追尾開始した時

仕様と注意点

  • 見出しが設定値より少ない・表示する設定にしていないページの時はモジュール全体を非表示にします
  • 目次記法による目次が表示されていない時のページ内リンクはsection0, section1...になります
  • ※追記(2019/08/08) 目次記法のリンクにもスムーズスクロールや履歴の設定が適用されます
    • ※目次を表示しない設定のページでは適用されません
  • 目次やメニューの開閉などでページの高さが後から変わったりすると現在位置表示はずれます
    • ※追記(2018/05/03) CLICK_UPDATEオプションである程度対応可能 解説↑
  • 沢山サイドバーモジュールを設置していると見づらいので置き過ぎには注意しましょう

タッチデバイスでの動作

レスポンシブデザインモードやPro機能、「PC版サイト表示」などをしない限りあまり関係ない話。

※追記(2019/08/08) タッチデバイスで非表示にする設定を追加TOUCH_DEVICE_DISABLE

スマホなどタッチデバイスでのスクロール処理はPCと違うので追尾が不自然になったりします。これを解決するためにタッチデバイスの時はposition:stickyIE11未対応)を使っているのですが、そのために以下の処理が入ります↓

  • #stoc-module#stoc.touchクラスを追加
  • ブラウザがposition:stickyに対応していれば#stoc-module.stickyクラスを追加
    • サイドバーモジュールの親要素#box2-innerの高さを記事部分#main-innerの高さと合わせる

#box2-innerの高さを変更した影響でテーマによっては見た目が変になりますが仕様です。

h2タグを使用する場合(2019/11/11追記)

はてなブログでh2タグを使用する場合、h2タグが使用されていないページで見た目を揃えるために見出しタグを書き換えるスクリプトh3→h2, h4→h3...のように)を使う場合があります。

このスクリプトと追尾する目次スクリプトを同時に使用すると、最初に取得した見出し要素が後から書き換えられ、見出し位置の取得ができなくなってしまいます。

なのでDELAY_GET_DOMオプションをオンにして書き換えスクリプトが実行されたあとに見出し要素を取得することでこの問題を回避できます。

変数名 説明
DELAY_GET_DOM 見出しなどを取得するタイミングをHTMLが読み込まれたあとにする


簡易なスクリプトの処理順
  • HTMLを順番に読み込み
    • 読み込まれたスクリプトが順番に実行される
    • [追尾目次] 見出し要素などを取得
  • HTMLの読み込みと解析が完了(DOMContentLoadedイベント
    • イベント後に実行されるスクリプトjQuery$()など)
    • [追尾目次] 追尾の設定や見出し位置を取得(DELAY_TIME_DOM
      • DELAY_GET_DOMがオンの場合見出し要素取得もここで
  • 画像など全ての読み込みが完了(loadイベント
    • イベント後に実行されるスクリプトjQuery$().on('load')など)
    • [追尾目次] もう一度追尾の設定や見出し位置を取得(DELAY_TIME_LOAD

こんなときは

デザイン・色が合わない

テーマによっては追尾中に背景色がなくなるなどの症状が出ます。

簡易的ですが以下の公式テーマ向けのCSS例(記事執筆時点)→ カスタマイズ例

  1. Aero
  2. Afternoon
  3. ヨミカキ by kanahei
  4. Popcorn by カタノトモコ
  5. Report
  6. レトロポップ
  7. Skull wings
  8. Solid
  9. Terminal
  10. Venetia

目次の上下に白いグラデーションが表示される

ページの縦幅よりも目次が大きい時にスクロールバーとさらにスクロール可能かを分かりやすくするためのオプション機能(SCROLL_SHADOW)があるんですが、
f:id:Twilyze:20180115011054p:plain
デフォルトでは白い背景色に合うように設定されているのでテーマに合わせて変更する必要があります。

[デザインCSS]に追加した以下の部分を

  • /* Shadows */の下に2つあるrgba(0,0,0,.17)(透明度のある黒)を表示したい色
  • /* Shadow covers */の下に2つある#fff(真っ白)を背景色と同じ色

にそれぞれ設定します

/* sidebar_toc.css */
#stoc.shadow {
  /* Shadows */
  background:
    radial-gradient(farthest-side at top, rgba(0,0,0,.17), transparent) top / 100% 11px,
    radial-gradient(farthest-side at bottom, rgba(0,0,0,.17), transparent) bottom / 100% 11px;
}
#stoc.shadow > ol {
  /* Shadow covers */
  background:
    linear-gradient(#fff 30%, transparent) top / 100% 40px,
    linear-gradient(transparent, #fff 70%) bottom / 100% 40px;
}

この表示自体いらない場合は設定部分のSCROLL_SHADOWfalseにしてください。

フッタに空の要素が表示される

フッタにスクリプトを書いた影響でテーマによっては空の#bottom-editarea要素が表示されます。非表示にしたい場合は[デザインCSS]に以下を追加してください。

#bottom-editarea {
  display: none;
}

フッタに他の要素がある場合動作がおかしくなるかも知れないので注意。

もしくはフッタではなくHTMLを書いた場所にスクリプトを追記するようにしても動きます。たぶん。

追尾する位置になると画面外に消える

親要素にアニメーションなどでtransformが使われているとposition:fixedが上手く動かなくなり表示できません。

position:fixedが効かない事件簿 - Qiita

position:stickyを使うモードにすると治る可能性があります(F_STICKY_MODE)。
親要素にoverflow:hiddenがあると動かないので注意。

なにかいい方法があるかも知れませんが基本的に親要素にtransformは使用しないでください。

あとがき

まさかの大型バージョンアップ3回目。もうここまで大きな更新はしない(はず)。

なるべく分かりやすいように作りたかったけど分かりづらいですね申し訳ない。各自頑張って…。
正直標準の公式テーマ対応するだけで力尽きたので他のテーマだとおかしいかも。


要望やバグ報告はコメント欄かGoogleフォームTwitterへ。
ただし対応できるかは分からないのであまり期待せずにどうぞ。

参考