是否有一个JavaScript解决方案来生成页面的"目录"?

Tho*_*ens 25 javascript

<h1>通过<h6>标签有标题.有没有一种方法可以使用JavaScript来生成用作锚标签的内容的目录?

我希望输出类似于:

<ol>
    <li>Header 1</li>
    <li>Header 1</li>
        <li>Header 2</li>
            <li>Header 3</li>
</ol>
Run Code Online (Sandbox Code Playgroud)

我目前没有使用JavaScript框架,但我不明白为什么我不能使用它.

我也在寻找已经完成的事情,因为我猜这是一个常见的问题,但如果没有,那么我自己的起点就会很好.

Ate*_*ral 35

我无法抗拒快速实施.

在页面的任何位置添加以下脚本:

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;
};
Run Code Online (Sandbox Code Playgroud)

您的页面应该是这样的结构:

<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>
Run Code Online (Sandbox Code Playgroud)

您可以在http://magnetiq.com/exports/toc.htm上看到它的运行情况(在IE,FF,Safari,Opera中工作)


Hen*_*rik 7

我已经修改了 AtesGoral 接受的答案中的函数,以输出正确的嵌套列表和有效的 HTML5。

只需将下面的代码添加到您的脚本中并TableOfContents(container, output);在加载时调用,其中容器是内容元素的类或 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;
};
Run Code Online (Sandbox Code Playgroud)


小智 6

我认为JQuery是一种快速简便的解决方案.快速谷歌搜索jquery目录产生两个有希望的结果:


d13*_*d13 5

这是一个很好的脚本来做到这一点:

https://github.com/matthewkastor/html-table-of-contents/wiki

要使用它:

  1. 添加这个标签:

    <script src="./node_modules/html-table-of-contents/src/html-table-of-contents.js" type="text/javascript">
    
    Run Code Online (Sandbox Code Playgroud)
  2. 调用该函数,例如在您的 body 的 onload 属性中:

    <body onload="htmlTableOfContents();"> 
    
    Run Code Online (Sandbox Code Playgroud)

以下是生成方法的定义:

/**
 * 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
}
Run Code Online (Sandbox Code Playgroud)


Has*_*örk 5

我真的很喜欢d13 的答案,但想稍微改进它以使用 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
}
Run Code Online (Sandbox Code Playgroud)

您可以通过将脚本包含在标头中来使用它。

很棒的是,您可以在目录中使用样式表:

<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>
Run Code Online (Sandbox Code Playgroud)

在我的个人脚本中,我使用了略有不同的选择器:

var headings = [].slice.call(documentRef.body.querySelectorAll("article h1, article h2, article h3, article h4, article h5, h6"));
Run Code Online (Sandbox Code Playgroud)

主页保留在内部<article></article>,脚本将仅搜索主文章内的标题。我可以在目录中使用标题,例如<nav id="toc"><h3>Table of contents</h3></nav>在页脚/页眉中使用 或 在页脚/页眉中使用,而无需将其显示在目录中。

在下面的评论中,Gabriel Hautclocq对文章代码进行了优化。他还指出,这并不是严格意义上的列表,而是类似列表的 div 结构,它更简单并且生成的代码更短。


CMS*_*MSG 5

这是我为工作而想出的目录的普通 JavaScript 版本。

它查找指定内容容器中的所有标题并为它们创建 ID,然后生成目录链接。我让它添加样式类并使用嵌套列表来创建层次结构。

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";
    });
}
});
Run Code Online (Sandbox Code Playgroud)

我在CodePen上有一个演示。