3

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

谢谢。

4

3 回答 3

4

first of all, you need to close your h1,h2 tags =)

if you do $("h1, h2, h3, h4, h5, h6") you'll get the tags in the same order they appear on the document. So you could do a loop on that array and check the level. e.g: if the last tag was H1 and you find a H2, it means that you need to create a <ol>. On the other hand, if you have a H3 and the next one is an H2, it means that you need to close the <ol>.

The hardest part would be to close the remaining <ol> in the end.

Hope this helps.

于 2011-04-28T14:14:12.797 回答
1

Don't want to go too into depth at the moment until we see what you've come up with so far.

However, look into .each() .children() and .find(). These should give you some ideas of how to accomplish what you're looking to do.

于 2011-04-28T14:13:59.893 回答
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>
于 2020-01-31T14:53:02.893 回答