最近我在blog中加上了目錄(TOC = Table Of Contents)的功能,它會自動找出網頁的heading(h1, h2, ...)並加上anchor,另外也會自動生成目錄的列表。雖然只是小功能,可是對於瀏覽文章還蠻方便的。下面是實作這個功能一些相關的筆記。
首先我先解釋一下為什麼不用AngularJS來做這個功能。如果看過這個blog的source,你會發現基本上這個blog是用html加上AngularJS做的,不過如果要生成TOC,並不適合使用AngularJS來處理。因為我不想在寫文章的時候,還要在heading的元件額外加上directive,所以生出的html heading只會是普通的heading,要在html找出特定元件不是AngularJS的使用情境,所以最後我選了其它的js外掛來處理這個功能。(好啦…其實是因為我懶的寫…)
其實有很多TOC相關的js外掛可以用,不過最後我選了contents.js。使用的方式可以參考repo的example(不要看README的Demo,因為Demo的範例反而沒有更新)。下面是目前blog使用的js:
$(function () {
var contents,
articleId;
articleId = function (articleName, element) {
return md5(articleName);
};
contents = gajus
.Contents({articleId: articleId});
$('.tos').append(contents.list());
});
contents.js 好用的地方就是很多東西可以客製化,例如我想把生出來的目錄放在 div.tos
之中,那只要加上 $('.tos').append(contents.list());
就可以了。另一個客製化的地方是下面這個:
articleId = function (articleName, element) {
return md5(articleName);
};
contents = gajus
.Contents({articleId: articleId});
contents.js 預設是去找heading中的文字並轉成anchor的id,不過它無法處理中文的heading。還好 contents.js 可以客製化id的生成方式,我直接使用JavaScript MD5(這個js可以處理UTF的文字)將heading的文字轉成md5 string,這樣就可以用嘍。
上面客製化 articleId 的方法仍然有問題,如果兩個 heading 有相同的文字,則 TOC 裡會產生相同的 md5 anchor 而造成連結都會被指向第一個的 heading。解決的方式就是將 articleId 改成下面這樣:
articleId = function (articleName, element) {
return md5(articleName + element.previousSibling.innerText + element.nextSibling.innerText);
};
contents = gajus
.Contents({articleId: articleId});
也就是產生 articleId 的方式除了參考原本在 heading 的文字外,還參考了 heading 前後 DOM 包含的文字,這樣就可以大大減少重複的可能性。(如果連前後文都一樣,就只能說遺憾了…)