【问题标题】:Any good codes to generate TOC from HTML heading elements in JavaScript?任何从 JavaScript 中的 HTML 标题元素生成 TOC 的好代码?
【发布时间】:2011-08-14 18:10:24
【问题描述】:

我想在 JavaScript 中生成如下 TOC:

<ol>
  <li>Heading 1</li>
  <li>Heading 2
    <ol>
      <li>Heading 2-1</li>
      <li>Heading 2-2</li>
    </ol>
  </li>
  <li>Heading 3</li>
</ol>

以及上面生成 TOC 的 HTML 代码:

<section id="toc">
  <p>This will be replaced with generated TOC.
</section>

<article>
  <h1>Heading 1<h1>
  <p>Bla bla bla.</p>

  <h1>Heading 2<h1>
  <p>Bla bla bla.</p>

    <h2>Heading 2-1<h2>
    <p>Bla bla bla.</p>

    <h2>Heading 2-2<h2>
    <p>Bla bla bla.</p>

  <h1>Heading 3<h1>
  <p>Bla bla bla.</p>
</article>

我真的被困住了:( 您如何编写代码来生成 TOC?我更喜欢 jQuery 或纯 JavaScript。

更新

这对我来说相当困难,但不知何故我想我已经做到了:

  $(function () {
    var assigned_level = 0,
        current_level = 0,
        id_number = 1,
        parent_node = "article",
        toc_html = '';

    $(parent_node + " *").each(function () {
      if (this.nodeName.length === 2 && this.nodeName.charAt(0) === "H") {
        $(this).attr("class", "heading");
      }
    });

    $(".heading").each( function () {
      current_level = this.nodeName.charAt(1);

      $(this).attr('id', "toc-" + id_number);

      // Close a list if a same level list follows.
      if (assigned_level !== current_level - 1) {
        toc_html += "</li>"
      }

      // Open parent lists if a child list follows.
      while (assigned_level < current_level) {
        toc_html += "<ol>";
        assigned_level += 1;
      }

      // Close child lists and the parent list if
      // the same level parent list follows.
      while (assigned_level > current_level) {
        toc_html += "</ol></li>";
        assigned_level -= 1;
      }

      toc_html += 
        '<li><a href="#' + this.id + '">' + $(this).html() + "</a>";
      id_number += 1;
    });

    // Close everything
    while (assigned_level > 0) {
      toc_html += "</li></ol>";
      assigned_level -= 1;
    }

    $("#toc").html(toc_html);
  });

我仍然不明白我做了什么 :P 也许有更复杂的方法。 请指出你发现的任何东西。

谢谢。

【问题讨论】:

  • 到目前为止你做了什么?
  • 我已经添加了 jQuery 标签,因为您可以使用 jQuery。这会让更多人关注它。
  • 这感觉很多就像一个“请为我写这个”的伪问题。如果你被卡住了,这表明你已经尝试了一些东西。向我们展示您的尝试,我们很乐意帮助您解决问题。
  • 哇。这么多cmets。谢谢大家,很抱歉这个愚蠢的问题。是的,我很懒:P
  • 看看HTML5 Outliner 可能会有所帮助。

标签: javascript jquery tableofcontents


【解决方案1】:

首先,你需要关闭你的 h1,h2 标签 =)

如果您执行$("h1, h2, h3, h4, h5, h6"),您将按照它们在文档中出现的顺序获得标签。因此,您可以对该数组进行循环并检查级别。例如:如果最后一个标签是 H1 并且您找到了 H2,则意味着您需要创建一个 &lt;ol&gt;。另一方面,如果你有一个H3,下一个是H2,那就意味着你需要关闭&lt;ol&gt;

最难的部分是最后关闭剩余的&lt;ol&gt;

希望这会有所帮助。

【讨论】:

  • “最困难的部分是最后关闭剩余的&lt;ol&gt;。” 不,只保留一个计数器,打开ol时将其递增,递减它在关闭一个时,在结束循环直到它是0。仅当 OP 编写了一个在字符串中创建标记然后将整个内容插入到文档中的解决方案(这往往会更快)时,这一切才有意义;如果您逐个对象构建它,显然您不必担心关闭标签,因为没有标签(只有元素)。
  • 啊逐个对象!!!我怎么没想到呢?我在服务器端工作太多了 XD thxs @T.J.克劳德
  • 嗯,问题是,构建一个大的标记字符串,然后将整个东西一次放入 DOM 通常更快——也许快 很多——比逐个对象构建 DOM 树。所以如果我这样做,我会按照你说的做,建立标记(可能在一个数组中,尽管这是否有帮助因浏览器而有很大差异),然后立即转储整个东西(如果在数组中,使用.join('') 创建单个字符串)。
【解决方案2】:

在我们看到您到目前为止的想法之前,目前不想太深入。

但是,请查看 .each() .children().find()。这些应该会给你一些关于如何完成你想做的事情的想法。

【讨论】:

    【解决方案3】:

    我知道这个问题已有 8 年历史了,但这是一个命题。 可能不是最有效的方法,也不是最短的方法,但它确实有效。

    • 首先我得到一个包含所有标题的数组。
    • 然后我通过添加每个标题的级别、ID 和父级来准备该数组。
    • 然后我将该数组转换为递归的分层数组。
    • 然后我生成 HTML 有序列表。
    • 然后将列表添加到目录中。

    在页面末尾添加以下 JavaScript:

    // prepare the array by adding level, ID and parent to each item of the array
    function prepare( array ) {
        let idt, level, t;
        for( let i = 0, n = array.length; i < n; i++ ) {
            t       = array[ i ];
            t.el    = t;
            level   = parseInt( t.tagName[1], 10 );
            t.level = level;
            t.idt   = i + 1;
    
            if( level <= 1 ) t.parent = 0;
            if( i ) {
                if( array[ i - 1 ].level < level ) {
                    t.parent = array[ i - 1 ].idt;
                } else if( array[ i - 1 ].level == level ) {
                    t.parent = array[ i - 1 ].parent;
                } else {
                    for( let j = i - 1; j >= 0; j-- ) {
                        if( array[ j ].level == level - 1) {
                            t.parent = array[ j ].idt;
                            break;
                        }
                    }
                }
            }
        }
        return array;
    }
    
    // transform a flat array in a hierarchical array
    function hierarchical( items ) {
        let hashTable = Object.create( null );
        items.forEach( item => hashTable[ item.idt ] = { ...item, subitems : [] } );
        let tree = [];
        items.forEach( item => {
            if( item.parent )
                hashTable[ item.parent ].subitems.push( hashTable[ item.idt ] );
            else
                tree.push(hashTable[ item.idt ]);
        });
        return tree;
    }
    
    // return an UL containing each title in a LI and possibly other items in UL sub-lists.
    function add_list( titles ) {
        let li, a, anchor;
        let ol = document.createElement( "ol" );
        if( titles && titles.length ) {
            for( t of titles ) {
                if( t.el.id ) anchor = t.el.id;
                else anchor = t.el.textContent;
                if( ! anchor ) anchor = "inconnu";
                anchor = anchor.replace( /\W/g, "" );
                t.el.id = anchor;
                li = document.createElement( "li" );
                a  = document.createElement( "a"  );
                a.href = `#${anchor}`;
                a.innerHTML = t.el.textContent;
                li.append( a );
                if( t.subitems && t.subitems.length ) {
                    li.append( add_list( t.subitems ) );
                }
                ol.append( li );
            }
        }
        return ol;
    }
    
    //get the toc element
    let divtoc = document.getElementById( "toc" );
    
    // get the article element
    let article  = document.getElementById( "article"  );
    if( toc && article ) {
        let titles = article.querySelectorAll( "h1, h2, h3, h4, h5, h6" );
        titles = prepare( titles );
        titles = hierarchical( titles );
        let ol_racine = add_list( titles );
        toc.append( ol_racine );
    }
    

    你会得到预期的结果:

    <section id="toc">
        <ol>
            <li><a href="#Heading1">Heading 1</a></li>
            <li>
                <a href="#Heading2">Heading 2</a>
                <ol>
                    <li><a href="#Heading21">Heading 2-1</a></li>
                    <li><a href="#Heading22">Heading 2-2</a></li>
                </ol>
            </li>
            <li><a href="#Heading3">Heading 3</a></li>
        </ol>
    </section>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-04-03
      • 1970-01-01
      • 2020-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-09
      相关资源
      最近更新 更多