从JavaScript中的HTML标题元素生成TOC的任何好代码?

时间:2011-04-28 13:58:19

标签: javascript jquery tableofcontents

我想在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也许有更复杂的方法。 请指出你找到的任何东西。

感谢。

3 个答案:

答案 0 :(得分:4)

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

如果您执行$("h1, h2, h3, h4, h5, h6"),您将按照文档中显示的相同顺序获取标记。所以你可以对该数组进行循环并检查级别。例如:如果最后一个标签是H1并且您找到H2,则表示您需要创建<ol>。另一方面,如果您有H3而下一个是H2,则表示您需要关闭<ol>

最难的部分是最终关闭剩余的<ol>

希望这有帮助。

答案 1 :(得分:1)

在我们看到你到目前为止所得到的内容之前,不要太深入。

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

答案 2 :(得分:0)

我知道这个问题已有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>