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

问题描述 投票:23回答:12

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

我希望输出类似于:

<ol>
    <li>Header 1</li>
    <li>Header 1</li>
        <li>Header 2</li>
            <li>Header 3</li>
</ol>

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

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

javascript
12个回答
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中工作))


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);
}

将以下代码添加到函数中以查找所有直通标记并将其设置为标题。

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);
}
};
Source:How to Create Table of Contents Using JavaScript

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);

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);

6
投票

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


4
投票

这是一个很棒的脚本:

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">
  2. 调用函数,例如在你的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
投票

我已经修改了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;
};

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内部。


2
投票

所以我对@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();
  }
  ); 

1
投票

您是在寻找预先打包的解决方案还是在询问如何实施?

对于后者,你可以使用 getElementsByTagName()通过<h1>递归地在<h6> XPath迭代所有<h*>元素并构造相应的嵌套<ul><ol>列表。您还必须将<a>标记添加到标题中。


0
投票

页面加载后,循环访问DOM并查找您感兴趣的元素。建立一个很好的锚点列表,并将其添加到您想要的位置的文档中。


0
投票

在这个页面上查看您需要的组件:Re-inventing XMLHttpRequest: Cross-browser implementation with sniffing capabilities

它遍历整个文档并创建一个TOC,其中所有h1-h6元素都在可打开(悬停)结构中反映出来。该组件是独立的,不使用任何库。

© www.soinside.com 2019 - 2024. All rights reserved.