Есть ли решение JavaScript для создания "оглавления" для страницы?
У меня есть заголовки в тегах <h1>
через <h6>
. Есть ли способ, которым я могу использовать JavaScript для создания оглавления для содержимого, которое также служит тегом привязки?
Я хотел бы, чтобы результат был чем-то вроде:
<ol>
<li>Header 1</li>
<li>Header 1</li>
<li>Header 2</li>
<li>Header 3</li>
</ol>
В настоящее время я не использую фреймворк JavaScript, но я не понимаю, почему я не мог его использовать.
Я также ищу что-то, потому что я предполагаю, что это обычная проблема, но если нет, отправной точкой для моего собственного использования было бы хорошо.
Ответы
Ответ 1
Я не мог удержаться от быстрой реализации.
Добавьте следующий script в любом месте вашей страницы:
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>
Вы можете увидеть его в действии на http://magnetiq.com/exports/toc.htm (работает в IE, FF, Safari, Opera)
Ответ 2
JQuery приходит на ум как быстрое и простое решение. Быстрый поиск google для содержимого jQuery дает два многообещающих результата:
Ответ 3
Здесь большой script для этого:
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">
-
Вызовите функцию, например, в свой собственный атрибут 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
}
Ответ 4
Вы ищете готовое решение или спрашиваете, как это можно реализовать?
Для последнего вы можете использовать getElementsByTagName()
рекурсивно на <h1>
через <h6>
XPath для повторения всех элементов <h*>
и построить соответствующие вложенные <ul>
или <ol>
списки. Вам также нужно добавить теги <a>
в заголовки.
Ответ 5
Я изменил функцию в принятом AtesGoral ответе на вывод правильно вложенных списков и допустимый HTML5.
Просто добавьте приведенный ниже код в свои сценарии и вызовите TableOfContents(container, output);
при загрузке, где контейнер является классом или идентификатором вашего элемента контента, а output - это класс или идентификатор элемента TOC. Значения по умолчанию: "#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;
};
Ответ 6
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);
Ответ 7
после загрузки страницы, пройдите через DOM и найдите интересующие вас элементы. создайте хороший список якорей и добавьте его в документ по желанию.
Ответ 8
Посмотрите компонент, который вы ищете на этой странице: Повторное создание XMLHttpRequest: кросс-браузерная реализация с возможностями нюхания
Он просматривает весь документ и создает TOC со всеми элементами h1-h6, отраженными в структуре openable (on hover). Компонент является автономным и не использует библиотеку.
Ответ 9
Вы можете создать динамическое оглавление для любого HTML-документа с помощью 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);
}
Ответ 10
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);
Ответ 11
Мне действительно нравится ответ Sridhar-Sarnobat, но хотел бы немного улучшить его, чтобы использовать нотации 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
}
Вы используете его, включив script в свой заголовок.
Удивительно, вы можете использовать таблицы стилей для вашего оглавления:
<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>
В моем личном script, я использую несколько иной селектор:
var headings = [].slice.call(documentRef.body.querySelectorAll("article h1, article h2, article h3, article h4, article h5, h6"));
Основная страница хранится внутри <article></article>
, а script будет искать заголовки только в основной статье. Я могу использовать заголовок внутри оглавления, например <nav id="toc"><h3>Table of contents</h3></nav>
или в нижнем колонтитуле/заголовке без его появления внутри toc.