本人为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>
输出结构以下: