我在<h1>
到<h6>
标记中有标题。有没有办法可以使用JavaScript来生成用作锚标签的内容的目录?
我希望输出类似于:
<ol>
<li>Header 1</li>
<li>Header 1</li>
<li>Header 2</li>
<li>Header 3</li>
</ol>
我目前没有使用JavaScript框架,但我不明白为什么我不能使用它。
我也在寻找已完成的事情,因为我猜这是一个常见的问题,但如果没有,那么我自己的起点就会很好。
答案 0 :(得分:33)
我无法抗拒快速实施。
在页面的任何位置添加以下脚本:
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中工作)中看到它的实际效果。)
答案 1 :(得分:6)
JQuery是一种快速简便的解决方案。快速谷歌搜索 jquery目录会产生两个有希望的结果:
答案 2 :(得分:4)
这是一个很棒的脚本:
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属性中:
<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
}
答案 3 :(得分:3)
我已经修改了AtesGoral接受的答案中的功能,以正确输出嵌套列表和有效的HTML5。
只需将以下代码添加到您的脚本中,然后在加载时调用TableOfContents(container, output);
,其中容器是您的内容元素的类或ID,输出是TOC元素的类或id。默认值为&#39; #content&#39;和&#39;#toc&#39;分别。
有关正常工作的演示,请参阅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;
};
答案 4 :(得分:2)
我真的很喜欢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内。
答案 5 :(得分:2)
所以我对@Ates Goral和@Hendrik提供的答案有疑问,我使用WYSIWYG,它在H1-h6元素中添加了一些html代码片段,例如br标签和其余标签。因此,代码将中断,并且由于与搜索模式不匹配而无法将其识别为有效的h元素。另外,某些WYSIWYG留下了空的H标签,由于它们没有内容,因此不排除在外。并且随后进行的各种修改经常会遇到相同的问题。 我修复的一个主要错误是,如果标题相同,则仅引用@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();
}
);
答案 6 :(得分:1)
您是在寻找预先打包的解决方案,还是在询问如何实施?
对于后者,您可以在getElementsByTagName()
到<h1>
XPath上递归使用 <h6>
迭代所有<h*>
元素并构造相应的嵌套的<ul>
或<ol>
列表。您还必须将<a>
标记添加到标题中。
答案 7 :(得分:1)
这是我为工作想出的目录的普通 JavaScript 版本。
它查找指定内容容器中的所有标题并为它们创建 ID,然后生成 TOC 链接。我让它添加样式类并使用嵌套列表来创建层次结构。
window.addEventListener('DOMContentLoaded', function (event) { // Let the DOM content load before running the script.
//Get all headings only from the actual contents.
var contentContainer = document.getElementById('content'); // Add this div to the html
var headings = contentContainer.querySelectorAll('h1,h2,h3,h4'); // You can do as many or as few headings as you need.
var tocContainer = document.getElementById('toc'); // Add this div to the HTML
// create ul element and set the attributes.
var ul = document.createElement('ul');
ul.setAttribute('id', 'tocList');
ul.setAttribute('class', 'sidenav')
// Loop through the headings NodeList
for (i = 0; i <= headings.length - 1; i++) {
var id = headings[i].innerHTML.toLowerCase().replace(/ /g, "-"); // Set the ID to the header text, all lower case with hyphens instead of spaces.
var level = headings[i].localName.replace("h", ""); // Getting the header a level for hierarchy
var title = headings[i].innerHTML; // Set the title to the text of the header
headings[i].setAttribute("id", id) // Set header ID to its text in lower case text with hyphens instead of spaces.
var li = document.createElement('li'); // create li element.
li.setAttribute('class', 'sidenav__item') // Assign a class to the li
var a = document.createElement('a'); // Create a link
a.setAttribute("href", "#" + id) // Set the href to the heading ID
a.innerHTML = title; // Set the link text to the heading text
// Create the hierarchy
if(level == 1) {
li.appendChild(a); // Append the link to the list item
ul.appendChild(li); // append li to ul.
} else if (level == 2) {
child = document.createElement('ul'); // Create a sub-list
child.setAttribute('class', 'sidenav__sublist')
li.appendChild(a);
child.appendChild(li);
ul.appendChild(child);
} else if (level == 3) {
grandchild = document.createElement('ul');
grandchild.setAttribute('class', 'sidenav__sublist')
li.appendChild(a);
grandchild.appendChild(li);
child.appendChild(grandchild);
} else if (level == 4) {
great_grandchild = document.createElement('ul');
great_grandchild.setAttribute('class', 'sidenav__sublist');
li.append(a);
great_grandchild.appendChild(li);
grandchild.appendChild(great_grandchild);
}
}
toc.appendChild(ul); // add list to the container
// Add a class to the first list item to allow for toggling active state.
var links = tocContainer.getElementsByClassName("sidenav__item");
links[0].classList.add('current');
// Loop through the links and add the active class to the current/clicked link
for (var i = 0; i < links.length; i++) {
links[i].addEventListener("click", function() {
var current = document.getElementsByClassName("current");
current[0].className = current[0].className.replace(" current", "");
this.className += " current";
});
}
});
我在 CodePen 有一个演示。
答案 8 :(得分:0)
页面加载后,循环访问DOM并查找您感兴趣的元素。建立一个很好的锚点列表,并将其添加到您想要的位置的文档中。
答案 9 :(得分:0)
在此页面上查看您要查找的组件:Re-inventing XMLHttpRequest: Cross-browser implementation with sniffing capabilities
它遍历整个文档并创建一个TOC,其中所有h1-h6元素都在可打开(悬停)结构中反映出来。该组件是独立的,不使用任何库。
答案 10 :(得分:0)
您可以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);
}
&#13;
将以下代码添加到函数中以查找所有直通标记并将其设置为标题。
var headings;
if (document.querySelectorAll)
headings=document.querySelectorAll("h1, h2, h3, h4, h5, h6");
else
headings=findHeadings(document.body, []);
&#13;
使用以下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);
}
};
&#13;
答案 11 :(得分:0)
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);
答案 12 :(得分:0)
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);
答案 13 :(得分:0)
这是一个基于 jQuery 的函数,它分析提供的内容中的标题元素 <h1>
, <h2>
... 并返回带有 TOC 层次结构的 jQuery 对象,您可以将其附加到您的页面:
<div class="table_of_contents">
<ul>
<a href="#Level1_Heading">Level1 Heading</a>
<li>
<a href="#Level2_Heading">Level2 Heading</a>
<ul>
...
</li>
</ul>
</div>
它还通过插入不可见的 $content
锚点来修改 <a name="...">
,用户点击创建的目录项时会跳转到这些锚点。
function generate_toc($content) {
let $toc = $("<div>", {class: "table_of_contents"});
let level2$toc_item = {0: $toc};
let used_anchors = {};
$content.find("h1, h2, h3, h4, h5").each(function() {
// find out the level of heading
let level = parseInt($(this).prop("tagName").replace(/[^0-9]/gi, ""));
let heading_text = $(this).text();
// define the unique anchor id
let heading_anchor = heading_text.replace(/[^a-z0-9]/gi, "_");
while (heading_anchor in used_anchors) {heading_anchor += "_";}
used_anchors[heading_anchor] = true;
// add target point into main content
$(this).prepend($("<a>", {name: heading_anchor}));
// find the parent level for TOC item
let parent_level = level-1;
for (; !(parent_level in level2$toc_item); parent_level--);
// remove all jumped over levels
for (let l in level2$toc_item) {
if (parseInt(l) > parent_level) {
delete level2$toc_item[l];
}
}
let $parent = level2$toc_item[parent_level];
// create new TOC item inside parent's <ul>
level2$toc_item[level] = $("<li>").appendTo(
$parent.children("ul").length == 1
? $($parent.children("ul")[0])
: $("<ul>").appendTo($parent)
).append($("<a>", {href: `#${heading_anchor}`}).text(heading_text));
});
return $toc;
}
使用示例:
$("body").prepend(generate_toc("body"));