你想要做的是创建一个嵌套列表,从文档标题中按层次结构进行排列。这被称为(变体的)文档大纲。
使用DOM和DOMParser API在浏览器上实现此功能的简单方法如下(将其放在HTML页面中并编码为ES5以便于测试):
<!DOCTYPE html>
<html>
<head>
<title>Document outline</title>
</head>
<body>
<div id="outline"></div>
<script>
var str = "<html><body><h1>h1-1</h1><h2>h2-1</h2><h3>h3-1</h3><p>something</p><h1>h1-2</h1><h2>h2-2</h2><h3>h3-2</h3></body></html>";
function emitSAXLikeEvents(node, handler) {
handler.startElement(node)
for (var i = 0; i < node.children.length; i++)
emitSAXLikeEvents(node.children.item(i), handler)
handler.endElement(node)
}
var outline = document.getElementById('outline')
var rank = 0
var context = outline
emitSAXLikeEvents(
(new DOMParser()).parseFromString(str, "text/html").body,
{
startElement: function(node) {
if (/h[1-6]/.test(node.localName)) {
var newRank = +node.localName.substr(1, 1)
while (newRank <= rank--)
context = context.parentNode.parentNode
rank = newRank
var ol
if (context.children.length > 0)
ol = context.children[0]
else {
ol = document.createElement('ol')
context.appendChild(ol)
}
var li = document.createElement('li')
li.appendChild(
document.createTextNode(node.innerText))
ol.appendChild(li)
context = li
}
},
endElement: function(node) {}
})
</script>
</body>
</html>
我首先将你的片段解析为一个
Document
,然后遍历它以创建类似SAX的
startElement()
调用。在
startElement()
函数中,检查标题元素的级别是否高于最近创建的列表项(如果有)。然后在正确的层次结构级别上附加新的列表项,并可能创建一个
ol
元素作为其容器。请注意,此算法不能处理从层次结构中的
h1
跳转到
h3
之类的情况,但可以轻松地进行调整。
如果要在node.js上创建大纲/目录,则可以使代码在服务器端运行,但需要一个不错的HTML解析库(可以说是node.js的DOMParser polyfill)。还有
https://github.com/h5o/h5o-js和
https://github.com/hoyois/html5outliner包可用于创建大纲,尽管我没有测试过这些包。这些包据说也可以处理一些特殊情况,例如在
iframe
和
quote
元素中的标题元素,通常不希望将其包含在文档大纲中。
创建HTML5大纲的主题具有悠久的历史;例如,参见
http://html5doctor.com/computer-says-no-to-html5-document-outline/。HTML4的做法是不使用任何分段根(在HTML5术语中)包装元素进行分段,并将标题和内容放置在相同的层次结构级别上,这被称为“平面地球标记”。SGML具有用于处理
H1
、
H2
等级元素的
RANK
特性,并且可以通过简单情况下的类似HTML4的“平面地球标记”(例如仅允许一个
section
或另一个单一元素作为分段根)来推断省略的
section
元素,从而自动创建大纲。
var dom = new DOMParser().parseFromString(str, 'text/html'); var hs = Array.from(dom.body.querySelectorAll('h1,h2,h3,h4,h5,h6'))
或等效的方式。 - ninjagecko