ClassicMacな見た目のブログテーマを自作している当ブログ。スクロールすると右下に目次が表示されるようにしたいのでスクラッチで目次機能を追加してみた。例によってWebMakerを使用しパーツを個別に自作。
うろ覚えなJSの文法を思い出しつつ、ウェブをこねくり回して検索してなんとか作ったのがこちらのクソコード
var headers = document.querySelectorAll('.post-body h2, .post-body h3:not(.post-title), .post-body h4');
var toc = document.getElementById('tocinner');
var i = 0; var k = 0; var j = 0;
var n = 1;
var id, toca;
headers.forEach(function(htag){
if( htag.tagName == 'H2'){
id = 'h2-' + i;
i++;
htag.id = id;
toca = document.createElement('p');
toca.innerHTML = n + '<a href=#' + id + '>' + htag.textContent + '</a>';
toc.appendChild(toca);
n++;
}
if( htag.tagName == 'H3'){
id = 'h3-' + k;
k++;
htag.id = id;
toca = document.createElement('p');
toca.innerHTML = n + '<a href=#' + id + '>' + htag.textContent + '</a>';
toc.appendChild(toca);
n++;
}
if( htag.tagName == 'H4'){
id = 'h4-' + j;
j++;
htag.id = id;
toca = document.createElement('p');
toca.innerHTML = n + '<a href=#' + id + '>' + htag.textContent + '</a>';
toc.appendChild(toca);
n++;
}
});
動くものはできた。
というのはわかってる。アマチュアクオリティならこの程度ものなんだよお母さん。コードでやっていることはかんたんで、
- Bloggerの投稿セクションの.post-bodyにあるh2~h4タグを収集して
- idを追加
- #tocに、aタグhref=idで順次追加していく
ということ。H2,H3,H4のために用意したif文ではそれぞれほぼ同じ処理をおこなっているので、重複をswitch文を使用してスリム化
var headers = document.querySelectorAll('.post-body h2, .post-body h3:not(.post-title), .post-body h4');
var toc = document.getElementById('tocinner');
var Hid, toca;
var Hn = 0;
var Hi = 0; var Hk = 0; var Hj = 0;
var m = 0;
headers.forEach(function(htag){
switch(htag.tagName){
case 'H2':
Hn = 2;
Hi++;
m=Hi;
break;
case 'H3':
Hn = 3;
Hk++;
m=Hk;
break;
case 'H4':
Hn = 4;
Hj++
m=Hj;
break;
}
Hid = Hn + '-' + m;
htag.id = Hid;
toca = document.createElement('p');
toca.innerHTML = '<a href=#' + Hid + '>' + htag.textContent + '</a>';
toc.appendChild(toca);
});
この処理のために宣言している変数が無駄に多い気がする。もっと減らせないの?難しい処理をしているわけではないんだから。
変数を減らしたい
H2タグ、H3タグ、H4タグが混在している記事中で律儀に
「一個目のH2なら付与するidは2-1で、2個目なら2-2」
「H3だったら3-1,2、H4なら4-1,2,」
というふうに増えていくようにナンバリングしたかったが、
と気づいた。ポルナレフも言っていることだし、ようは順序通りに目次追加ができていて、リンクジャンプで記事のそれぞれヘッダーに飛べればOKじゃぁないかってことで、付与するidはヘッダータグの要素内の値の先頭5文字にしてしまうことにした。これで宣言する変数の数を減らしてしまえ。
function createToc(){
var headers = document.querySelectorAll('.post-body .post-title, .post-body h2, .post-body h3, .post-body h4');
var toc = document.getElementById('toc');
var tocinner = document.createElement('div');
var toca, Hn;
var n = 1;
tocinner.id="tocinner";
headers.forEach(function(htag){
switch(htag.tagName){
case 'H2':
Hn = 0; break;
case 'H3':
Hn = 2; break;
case 'H4':
Hn = 4; break; }
htag.id = htag.innerHTML.slice(1, 5) + n;
n++;
toca = document.createElement('p');
toca.innerHTML = '<a href="#' + htag.id + '" style="padding-left:' + Hn + 'px" >' + htag.textContent + '</a>';
tocinner.appendChild(toca);
});
toc.appendChild(tocinner);
}
createToc();
改善点としては、divタグを作ってそこへ収集したヘッダーを追加していって、DOMへ
追加するのは最後にまとめて一回だけ行うようにした。
個別記事ページでのみ目次を表示するようにする
すべてのヘッダータグを収集しているので、Topページ、ラベルページ、アーカイブページなど記事が複数表示されているページでは目次を表示したくない。URLに注目するとそれぞれいかのようになった。
- TopPage: https://thmertial.blogspot.com/
- 個別記事ページ: https://thmertial.blogspot.com/2020/04/bloggerhtml.html
- ラベル:https://thmertial.blogspot.com/search/label/ラベル名
- アーカイブ:https://thmertial.blogspot.com/2020/04/
個別記事ページがカレントページだとすると末尾に
htmlがつく。つまり、個別記事ページのURL末尾にhtmlがあれば個別記事ページにアクセスしていることが判定できる。
if ( location.href.lastIndexOf("html") > 0 ){
createToc();
}
Bloggerのネイティブコードに、個別記事かどうかを判定するコードがあったはずなのでそれをなんとか利用したいな。。。。
CSSで見た目はどうしよかなぁ
にょう!ってでてくる目次ですが、Macのウィンドウ
っぽい枠にするのもいいんだけどサイズ的にちいさいからなぁ・・・・・・。バランスが悪いなぁ・・・・。と思って、SheepShaverのスクショを漁っていると・・・・・みつけた!!!
クラリスワークスのワードアプリを起動中に出てきた文字変換候補ウィンドウ!これいいじゃん!!目次で表示されたらちょうどええやん!この変換候補ウィンドウに目次のヘッダーが入ってほしいぞ
「左側のボツボツはグラデーションのドット表現でいけるな!」
とか思ったら、さぁぁぁとWebMakerで完成させてしまった。微妙にずれがあるかもだけどだいたいこんなもんだろう。右側のスクロールバーを実装できてないんだけど、スクロールバーのCSSってどうやってつくるんだろうか・・・。