我通过<h1>
标签在<h6>
中有标题。有没有一种方法可以使用JavaScript来生成用作锚标签的内容的目录?
我希望输出类似于:
<ol>
<li>Header 1</li>
<li>Header 1</li>
<li>Header 2</li>
<li>Header 3</li>
</ol>
我目前没有使用JavaScript框架,但我不明白为什么我不能使用它。
我也在寻找已经完成的事情,因为我猜这是一个常见的问题,但如果没有,那么我自己的起点就会很好。
我无法抗拒快速实施。
在页面的任何位置添加以下脚本:
window.onload = function () {
var toc = "";
var level = 0;
document.getElementById("contents").innerHTML =
document.getElementById("contents").innerHTML.replace(
/<h([\d])>([^<]+)<\/h([\d])>/gi,
function (str, openLevel, titleText, closeLevel) {
if (openLevel != closeLevel) {
return str;
}
if (openLevel > level) {
toc += (new Array(openLevel - level + 1)).join("<ul>");
} else if (openLevel < level) {
toc += (new Array(level - openLevel + 1)).join("</ul>");
}
level = parseInt(openLevel);
var anchor = titleText.replace(/ /g, "_");
toc += "<li><a href=\"#" + anchor + "\">" + titleText
+ "</a></li>";
return "<h" + openLevel + "><a name=\"" + anchor + "\">"
+ titleText + "</a></h" + closeLevel + ">";
}
);
if (level) {
toc += (new Array(level + 1)).join("</ul>");
}
document.getElementById("toc").innerHTML += toc;
};
您的页面应该是这样的结构:
<body>
<div id="toc">
<h3>Table of Contents</h3>
</div>
<hr/>
<div id="contents">
<h1>Fruits</h1>
<h2>Red Fruits</h2>
<h3>Apple</h3>
<h3>Raspberry</h3>
<h2>Orange Fruits</h2>
<h3>Orange</h3>
<h3>Tangerine</h3>
<h1>Vegetables</h1>
<h2>Vegetables Which Are Actually Fruits</h2>
<h3>Tomato</h3>
<h3>Eggplant</h3>
</div>
</body>
你可以在https://codepen.io/scheinercc/pen/KEowRK上看到它(旧链接:http://magnetiq.com/exports/toc.htm(在IE,FF,Safari,Opera中工作))
你可以create dynamic table of contents for any HTML document using JavaScript,它可以显示从h1到h6的标题列表,带有标题的链接,并使用以下步骤更容易浏览文档。
首先创建window.onload函数,该函数在文档完成加载时自动运行,如下所示
window.onload=function(){
function getSelectedText(){
if (window.getSelection)
return window.getSelection().toString()+"
"+document.URL;
else if (document.selection)
return document.selection.createRange().text+"
"+document.URL;
}
var toc=document.getElementById("TOC");
if(!toc) {
toc=document.createElement("div");
toc.id="TOC";
document.body.insertBefore(toc, document.body.firstChild);
}
将以下代码添加到函数中以查找所有直通标记并将其设置为标题。
var headings;
if (document.querySelectorAll)
headings=document.querySelectorAll("h1, h2, h3, h4, h5, h6");
else
headings=findHeadings(document.body, []);
使用以下CSS代码设计目录
#TOC {border:solid black 1px; margin:10px; padding:10px;}
.TOCEntry{font-family:sans-serief;}
.TOCEntry a{text-decoration:none;}
.TOCLevel1{font-size:17pt; font-weight:bold;}
.TOCLevel2{font-size:16pt; font-weight:bold;}
.TOCLevel3{font-size:15pt; font-weight:bold;}
.TOCLevel4{font-size:14pt; margin-left:.25in;}
.TOCSectNum{display:none;}
这是一个完整的JavaScript代码,用于在脚本标记内创建目录。
window.onload=function(){
function getSelectedText(){
if (window.getSelection)
return window.getSelection().toString()+"<br/>"+document.URL;
else if (document.selection)
return document.selection.createRange().text+"<br/>"+document.URL;
}
var toc=document.getElementById("TOC");
if(!toc) {
toc=document.createElement("div");
toc.id="TOC";
document.body.insertBefore(toc, document.body.firstChild);
}
var headings;
if (document.querySelectorAll)
headings=document.querySelectorAll("h1, h2, h3, h4, h5, h6");
else
headings=findHeadings(document.body, []);
function findHeadings(root, sects){
for(var c=root.firstChild; c!=null; c=c.nextSibling){
if (c.nodeType!==1) continue;
if (c.tagName.length==2 && c.tagName.charAt(0)=="H")
sects.push(c);
else
findHeadings(c, sects);
}
return sects;
}
var sectionNumbers=[0,0,0,0,0,0];
for(var h=0; h<headings.length; h++) {
var heading=headings[h];
if(heading.parentNode==toc) continue;
var level=parseInt(heading.tagName.charAt(1));
if (isNaN(level)||level<1||level>6) continue;
sectionNumbers[level-1]++;
for(var i=level; i<6; i++) sectionNumbers[i]=0;
var sectionNumber=sectionNumbers.slice(0, level).join(".");
var span=document.createElement("span");
span.className="TOCSectNum";
span.innerHTML=sectionNumber;
heading.insertBefore(span, heading.firstChild);
heading.id="TOC"+sectionNumber;
var anchor=document.createElement("a");
heading.parentNode.insertBefore(anchor, heading);
anchor.appendChild(heading);
var link=document.createElement("a");
link.href="#TOC"+sectionNumber;
link.innerHTML=heading.innerHTML;
var entry=document.createElement("div");
entry.className="TOCEntry TOCLevel" + level;
entry.appendChild(link);
toc.appendChild(entry);
}
};
let headers = document.querySelectorAll('h1,h2,h3,h4,h5,h6');
let list = document.createElement('ol');
let _el = list;
for(i=0; i<headers.length; i++) {
while(_el) {
let li = document.createElement('li');
li.innerText = headers[i].innerText;
li.setAttribute('tagName', headers[i].tagName);
if(_el.getAttribute('tagName') < headers[i].tagName) {
let ol = _el.children.length > 0 ? ol = _el.querySelector('ol') : document.createElement('ol');
ol.appendChild(li);
_el.appendChild(ol);
_el = li;
break;
} else {
if(_el.tagName == 'OL') {
_el.appendChild(li);
_el = li;
break;
} else if (!_el.parentNode.parentNode) {
_el.parentNode.appendChild(li);
_el = li;
break;
}
else {
_el = _el.parentNode.parentNode;
}
}
}
}
console.log(list);
this.insert = (el, h) => {
let li = document.createElement('li');
li.innerText = h.innerText;
li.setAttribute('tagName', h.tagName);
if(el.tagName == 'OL') {
el.appendChild(li);
return li;
} else if(el.getAttribute('tagName') < h.tagName) {
let ol = el.children.length > 0 ? ol = el.querySelector('ol') : document.createElement('ol');
ol.appendChild(li);
el.appendChild(ol);
return li;
} else if(!el.parentNode.parentNode) {
el.parentNode.appendChild(li);
return li;
} else {
return this.insert(el.parentNode.parentNode, h);
}
}
this.parse = (headers) => {
let list = document.createElement('ol');
let el = list;
for(i=0; i<headers.length; i++) {
el = this.insert(el, headers[i]);
}
return list;
}
let headers = document.querySelectorAll('h1,h2,h3,h4,h5,h6');
let toc = this.parse(headers);
console.log(toc);
我认为JQuery是一种快速简便的解决方案。快速谷歌搜索jquery目录产生两个有希望的结果:
这是一个很棒的脚本:
https://github.com/matthewkastor/html-table-of-contents/wiki
要使用它:
<script src="./node_modules/html-table-of-contents/src/html-table-of-contents.js" type="text/javascript">
<body onload="htmlTableOfContents();">
以下是生成方法的定义:
/**
* Generates a table of contents for your document based on the headings
* present. Anchors are injected into the document and the
* entries in the table of contents are linked to them. The table of
* contents will be generated inside of the first element with the id `toc`.
* @param {HTMLDOMDocument} documentRef Optional A reference to the document
* object. Defaults to `document`.
* @author Matthew Christopher Kastor-Inare III
* @version 20130726
* @example
* // call this after the page has loaded
* htmlTableOfContents();
*/
function htmlTableOfContents (documentRef) {
var documentRef = documentRef || document;
var toc = documentRef.getElementById('toc');
var headings = [].slice.call(documentRef.body.querySelectorAll('h1, h2, h3, h4, h5, h6'));
headings.forEach(function (heading, index) {
var anchor = documentRef.createElement('a');
anchor.setAttribute('name', 'toc' + index);
anchor.setAttribute('id', 'toc' + index);
var link = documentRef.createElement('a');
link.setAttribute('href', '#toc' + index);
link.textContent = heading.textContent;
var div = documentRef.createElement('div');
div.setAttribute('class', heading.tagName.toLowerCase());
div.appendChild(link);
toc.appendChild(div);
heading.parentNode.insertBefore(anchor, heading);
});
}
try {
module.exports = htmlTableOfContents;
} catch (e) {
// module.exports is not defined
}
我已经修改了AtesGoral接受的答案中的函数,以正确输出嵌套列表和有效的HTML5。
只需将下面的代码添加到脚本中并在加载时调用TableOfContents(container, output);
,其中container是内容元素的类或id,输出是TOC元素的类或id。默认值分别为“#contents”和“#toc”。
有关工作演示,请参阅http://codepen.io/aufmkolk/pen/RWKLzr。
function TableOfContents(container, output) {
var toc = "";
var level = 0;
var container = document.querySelector(container) || document.querySelector('#contents');
var output = output || '#toc';
container.innerHTML =
container.innerHTML.replace(
/<h([\d])>([^<]+)<\/h([\d])>/gi,
function (str, openLevel, titleText, closeLevel) {
if (openLevel != closeLevel) {
return str;
}
if (openLevel > level) {
toc += (new Array(openLevel - level + 1)).join('<ul>');
} else if (openLevel < level) {
toc += (new Array(level - openLevel + 1)).join('</li></ul>');
} else {
toc += (new Array(level+ 1)).join('</li>');
}
level = parseInt(openLevel);
var anchor = titleText.replace(/ /g, "_");
toc += '<li><a href="#' + anchor + '">' + titleText
+ '</a>';
return '<h' + openLevel + '><a href="#' + anchor + '" id="' + anchor + '">'
+ titleText + '</a></h' + closeLevel + '>';
}
);
if (level) {
toc += (new Array(level + 1)).join('</ul>');
}
document.querySelector(output).innerHTML += toc;
};
我真的很喜欢Sridhar-Sarnobat's answer,但是想稍微改进它以使用html5表示法并保留标题的现有ID:
document.addEventListener('DOMContentLoaded', function() {
htmlTableOfContents();
} );
function htmlTableOfContents( documentRef ) {
var documentRef = documentRef || document;
var toc = documentRef.getElementById("toc");
// Use headings inside <article> only:
// var headings = [].slice.call(documentRef.body.querySelectorAll('article h1, article h2, article h3, article h4, article h5, article h6'));
var headings = [].slice.call(documentRef.body.querySelectorAll('h1, h2, h3, h4, h5, h6'));
headings.forEach(function (heading, index) {
var ref = "toc" + index;
if ( heading.hasAttribute( "id" ) )
ref = heading.getAttribute( "id" );
else
heading.setAttribute( "id", ref );
var link = documentRef.createElement( "a" );
link.setAttribute( "href", "#"+ ref );
link.textContent = heading.textContent;
var div = documentRef.createElement( "div" );
div.setAttribute( "class", heading.tagName.toLowerCase() );
div.appendChild( link );
toc.appendChild( div );
});
}
try {
module.exports = htmlTableOfContents;
} catch (e) {
// module.exports is not defined
}
您可以通过在标题中包含脚本来使用它。
非常棒的是,您可以使用样式表作为目录:
<style>
#toc div.h1 { margin-left: 0 }
#toc div.h2 { margin-left: 1em }
#toc div.h3 { margin-left: 2em }
#toc div.h4 { margin-left: 3em }
</style>
在我的个人脚本中,我使用了一个略有不同的选择器:
var headings = [].slice.call(documentRef.body.querySelectorAll("article h1, article h2, article h3, article h4, article h5, h6"));
主页面保存在<article></article>
内,脚本将只搜索主文章中的标题。我可以在目录中使用标题,如<nav id="toc"><h3>Table of contents</h3></nav>
或页脚/标题,而不会显示在toc内部。
所以我对@Ates Goral和@Hendrik提供的答案有疑问,我使用了WYSIWYG,它在H1-h6元素中添加了一些html片段,如br标签和其余部分。因此代码断开并且不将其识别为有效的h元素,因为它与搜索模式不匹配。还有一些WYSIWYG会留下空的h-tag,因为它们没有内容,所以不会被排除。并且随后的各种修改经常打破同样的问题。我修复的一个主要错误是,如果你有一个标题,使用相同的文本,它只会引用@Ates Goral和@Hendrik提供的第一个解决方案
我应该声明,如果要从存储在数据库中的数据生成内容表,这个解决方案是好的。我使用了上面的一些解决方案,特别是@Ates Goral和@Hendrik
function TableOfContents(container, output) {
var txt = "toc-";
var toc = "";
var start = 0;
var output = output || '#toc';
var container = document.querySelector(container) || document.querySelector('#contents');
var c = container.children;
for (var i = 0; i < c.length; i++) {
var isHeading = c[i].nodeName.match(/^H\d+$/) ;
if(c[i].nodeName.match(/^H\d+$/)){
var level = c[i].nodeName.substr(1);
// get header content regardless of whether it contains a html or not that breaks the reg exp pattern
var headerText = (c[i].textContent);
// generate unique ids as tag anchors
var anchor = txt+i;
var tag = '<a href="#' + anchor + '" id="' + anchor + '">' + headerText + '</a>';
c[i].innerHTML = tag;
if(headerText){
if (level > start) {
toc += (new Array(level - start + 1)).join('<ul>');
} else if (level < start) {
toc += (new Array(start - level + 1)).join('</li></ul>');
} else {
toc += (new Array(start+ 1)).join('</li>');
}
start = parseInt(level);
toc += '<li><a href="#' + anchor + '">' + headerText + '</a>';
}
}
}
if (start) {
toc += (new Array(start + 1)).join('</ul>');
}
document.querySelector(output).innerHTML += toc;
}
document.addEventListener('DOMContentLoaded', function() {
TableOfContents();
}
);
您是在寻找预先打包的解决方案还是在询问如何实施?
对于后者,你可以使用
getElementsByTagName()
通过<h1>
递归地在<h6>
XPath迭代所有<h*>
元素并构造相应的嵌套<ul>
或<ol>
列表。您还必须将<a>
标记添加到标题中。
页面加载后,循环访问DOM并查找您感兴趣的元素。建立一个很好的锚点列表,并将其添加到您想要的位置的文档中。
在这个页面上查看您需要的组件:Re-inventing XMLHttpRequest: Cross-browser implementation with sniffing capabilities
它遍历整个文档并创建一个TOC,其中所有h1-h6元素都在可打开(悬停)结构中反映出来。该组件是独立的,不使用任何库。