本人爲Javascript
菜鳥,有問題請指正...前端
最近用Flask
搭建我的博客在製做文章顯示頁面時,須要在前端頁面生成TOC。就打算本身寫一個JavaScript
腳本。算法
須要生成的目標HTML代碼以下:設計模式
<ul> <li><a href="#TOC1">標題1</a></li> <li><a href="#TOC2">標題2</a> <ul> <li><a href="#TOC2.1">標題2.1</a></li> <li><a href="#TOC2.1">標題2.2</a> <ul> <li><a href="#TOC2.2.1">標題2.2.1</a></li> </ul> </li> </ul> </li> ... </ul>
在看了JavaScript權威指南這本書後,發現其中雖然有例子生成TOC,可是並無顯示層級。不過,其中有一個記錄TOC各個錨點序號的好方法:數組
# 使用一個數組報錯<h1到h6標題出現的次數 var sectionNumbers = [0,0,0,0,0,0] # 取各級標題的level,如<h1>的level爲1 var level = parseInt(header.tagName.charAt(1)); # 當處理一個標籤後,計算加1 sectionNumbers[level-1]++ # 而後去當前序號 好比序號爲2.1.1 sectionNumber = sectionNumbers.slice(0,level).join('.')
吹牛B以前須要打草稿,寫代碼以前也如此。app
剛開始很糾結如何生成有層級結構的HTML代碼,最後發現可使用設計模式中的組合模式來完成這一功能。具體點就是將li節點看做葉子節點,ul節點看錯做容器。那麼就有一下限制:post
只有ul節點能夠插入li節點this
li節點須要插入子節點,就須要建立一個ul子節點,讓子節點去插入li節點spa
ul與它的li子節點level相同設計
若是當前節點須要處理一個header,就須要做出如下判斷:3d
判斷本身爲ul節點仍是li節點
若是爲ul節點,且待處理的header的level與本身相同,那麼就直接生成一個li節點並插入;若是待處理的header的level比本身大,那麼就找到子節點,交給子節點去處理。
若是爲li節點,且待處理的header的level比本身大,那麼就取li節點的ul節點,交給ul節點去處理
一直向下去處理header,直到插入成功
爲了儘可能減小代碼的污染,使用Fucntion.call(args)
方式了動態的給節點添加屬性:
var sectionNumbers = [0, 0, 0, 0, 0, 0]; function Toc() { this.headers = $(this).find(':header'); var ul = document.createElement('ul'); this.toc = TocObj.call(ul, 1); for(var i = 0; i < this.headers.length; i++) { // if(i > 1) break; this.toc.add(this.headers[i]); } console.log(this.toc); } function TocObj(level) { this.level = level; this.num = 0; this.add = function(header) { var flag = this.tagName == 'UL'; // ul節點和li節點處理header的方式不經過 var level = parseInt(header.tagName.charAt(1)); if(flag) { // 只有ul節點才能插入li if(this.num == 0 || level == this.level) { var link = document.createElement('a'); link.href = ''; link.innerHTML = header.innerHTML; sectionNumbers[level-1]++; for(var i = level; i < sectionNumbers.length; i++) sectionNumbers[i] = 0; link.href = "#TOC" + sectionNumbers.slice(0, level).join('.'); var li = document.createElement('li'); li.insertBefore(link, li.firstChild); this.num++; this.appendChild(TocObj.call(li, level)); } else if(level > this.level){ if(this.num == 0) { throw new Error("level error 1"); } var lastChild = this.lastChild; lastChild.add(header); // 讓ul節點的li節點去處理header } } else { // li節點讓它的ul子節點去插入li if(level == this.level) { throw new Error("level error 2"); } else if(level > this.level){ if(this.num == 0) { // 沒有ul子節點,就建立一個 var ul = document.createElement('ul'); this.appendChild(ul); this.num++; TocObj.call(ul, level).add(header); // 注意設置level } else { var lastChild = this.lastChild; lastChild.add(header); //讓ul節點去處理這個header } } } } return this; } var toc = document.getElementsByClassName('post')[0]; Toc.call(toc);
對於有如下結構的HTML代碼:
<div class="col-md-9 post"> <h1>標題1</h1> <h2>標題1.1</h2> <h2>標題1.2</h2> <h1>標題2</h1> <h2>標題2.1</h2> <h3>標題2.1.1</h3> <h1>標題3</h1> <h1>標題4</h1> <h1>標題5</h1> </div>
輸出結構以下: