有什么好的代码可以在 JavaScript 中从 HTML 标题元素生成目录吗?

发布于 2024-11-03 20:55:08 字数 2357 浏览 7 评论 0 原文

我想在 JavaScript 中生成如下所示的目录:

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

以及用于生成上面目录的 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>

我真的被困住了:( 如何编写生成目录的代码?我更喜欢 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也许有更复杂的方法。 请指出您发现的任何内容。

谢谢。

I'd like to generate TOC like below in JavaScript:

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

And the HTML codes to generate the TOC above:

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

I'm really stuck :(
How do you write the code to generate the TOC? I prefer jQuery or pure JavaScript.

UPDATE

This was quite tough for me, but somehow I suppose I've done:

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

I still don't understand what I've done :P Perhaps there's more sophisticated ways.
Please point me out anything you've found.

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

滴情不沾 2024-11-10 20:55:08

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

如果你这样做 $("h1, h2, h3, h4, h5, h6") 你会得到相同的标签它们出现在文档上的顺序。因此,您可以对该数组执行循环并检查级别。例如:如果最后一个标签是 H1,而您找到了 H2,则意味着您需要创建一个

    。另一方面,如果您有一个 H3,而下一个是 H2,则意味着您需要关闭

最难的部分是最终关闭剩余的

希望这有帮助。

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.

尘世孤行 2024-11-10 20:55:08

在我们看到您到目前为止所取得的成果之前,目前不想太深入。

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

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.

饮惑 2024-11-10 20:55:08

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

I know this question is 8 years old, but here's a proposition.
Probably not the most efficient way to do it, nor the shortest, but it works.

  • First I get an array of all the headings.
  • Then I prepare that array by adding the level, id and parent of each heading.
  • Then I transform that array into a recursive, hierarchical one.
  • Then I generate the HTML ordered lists.
  • Then add the list into the TOC.

In the end of your page, add the following 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 );
}

You'll get the intended result:

<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>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文