在部落格中加上目錄(TOC)功能
2016-06-19 11:11:38
TOC
TOC

前言

最近我在blog中加上了目錄(TOC = Table Of Contents)的功能,它會自動找出網頁的heading(h1, h2, ...)並加上anchor,另外也會自動生成目錄的列表。雖然只是小功能,可是對於瀏覽文章還蠻方便的。下面是實作這個功能一些相關的筆記。

How

Why not AngularJS

首先我先解釋一下為什麼不用AngularJS來做這個功能。如果看過這個blog的source,你會發現基本上這個blog是用html加上AngularJS做的,不過如果要生成TOC,並不適合使用AngularJS來處理。因為我不想在寫文章的時候,還要在heading的元件額外加上directive,所以生出的html heading只會是普通的heading,要在html找出特定元件不是AngularJS的使用情境,所以最後我選了其它的js外掛來處理這個功能。(好啦…其實是因為我懶的寫…)

使用contents.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,這樣就可以用嘍。

UPDATE 2016.09.28

上面客製化 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 包含的文字,這樣就可以大大減少重複的可能性。(如果連前後文都一樣,就只能說遺憾了…)