递归 JS 函数列出 Hx HTML 标签?

发布于 2024-10-27 07:30:41 字数 1519 浏览 1 评论 0原文

我有一些代码:

$('section').each(function() {
    var the_id;
    var list_item;
    $(this).find('h2').each(function() {
        the_id = $(this).attr('id');
        $('nav > ol').append('<li><a href="#' + the_id + '">' + $(this).text() + '</a></li>');
        $(this).siblings('h3').each(function(i) {
            the_id = $(this).attr('id');
            if (i == 0) {
                $('nav > ol > li:last').append('<ol></ol>');
            }
            $('nav > ol > li:last > ol').append('<li><a href="#' + the_id + '">' + $(this).text() + '</a></li>');
        });
    });
});

它会生成一些 HTML,例如:

<li>
   <a href="#example-1">Example 1</a>
   <ol>
      <li>
         <a href="#example-1a">Example 1a</a>
     </li>
     <li>
         <a href="#example-1b">Example 1b</a>
     </li>
   </ol>
</li>
<li>
   <a href="#another-example">Another Example</a>
</li>
<li>
  <a href="#last-one">Last One</a>
</li>
<li>
   <a href="#just-kidding--another">Just kidding, another</a>
   <ol>
      <li>
         <a href="#this-is-a-sub-header">This is a sub header</a>
      </li>
   </ol>
</li>

我的问题是,我的 JS 只执行我编写的内容(h2,然后查找 h3s,并且我必须为 另一个 编写回调做 h4 然后再做 h5 和 h6 我如何编写一个递归函数来执行此操作?

I have some code:

$('section').each(function() {
    var the_id;
    var list_item;
    $(this).find('h2').each(function() {
        the_id = $(this).attr('id');
        $('nav > ol').append('<li><a href="#' + the_id + '">' + $(this).text() + '</a></li>');
        $(this).siblings('h3').each(function(i) {
            the_id = $(this).attr('id');
            if (i == 0) {
                $('nav > ol > li:last').append('<ol></ol>');
            }
            $('nav > ol > li:last > ol').append('<li><a href="#' + the_id + '">' + $(this).text() + '</a></li>');
        });
    });
});

It generates some HTML like:

<li>
   <a href="#example-1">Example 1</a>
   <ol>
      <li>
         <a href="#example-1a">Example 1a</a>
     </li>
     <li>
         <a href="#example-1b">Example 1b</a>
     </li>
   </ol>
</li>
<li>
   <a href="#another-example">Another Example</a>
</li>
<li>
  <a href="#last-one">Last One</a>
</li>
<li>
   <a href="#just-kidding--another">Just kidding, another</a>
   <ol>
      <li>
         <a href="#this-is-a-sub-header">This is a sub header</a>
      </li>
   </ol>
</li>

My issue is, my JS only goes as far as i write it (h2, then looks for h3s, and i'd have to write another callback for doing h4's then another for h5's and h6's. How can I write a recursive function that does this?

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

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

发布评论

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

评论(3

话少心凉 2024-11-03 07:30:41

另一种构建 HTML 列表的方法怎么样?我真的不擅长编写/理解递归代码。迭代通常对我来说更容易。

function buildToc(container) {
    var html = "", lastLevel = -1;
    container.find('h1, h2, h3, h4, h5, h6, h7, h8, h9').each(function() {
        var $this = $(this);
        var level = this.tagName.match(/H(\d)/)[1];
        if (lastLevel < level) {
            html += "<ol>";
        }
        if (lastLevel > level)  {
            html += "</ol>";
        }
        html += "<li><a href='"+ this.id + "' >" + $this.text() + "</a></li>";
        lastLevel = level;
    });
    return html;
}
$('nav').append( buildToc( $('article section') ) );

我在您的页面上运行了它,它复制了您现有的目录。而且您不需要为每个级别编写自定义代码;又快又脏。

How about another approach to building the list HTML? I really suck at writing/understanding recursive code. Iteration is usually easier for me.

function buildToc(container) {
    var html = "", lastLevel = -1;
    container.find('h1, h2, h3, h4, h5, h6, h7, h8, h9').each(function() {
        var $this = $(this);
        var level = this.tagName.match(/H(\d)/)[1];
        if (lastLevel < level) {
            html += "<ol>";
        }
        if (lastLevel > level)  {
            html += "</ol>";
        }
        html += "<li><a href='"+ this.id + "' >" + $this.text() + "</a></li>";
        lastLevel = level;
    });
    return html;
}
$('nav').append( buildToc( $('article section') ) );

I ran that on your page and it duplicated your existing TOC. And you don't need custom code for each level; Quick and dirty.

无畏 2024-11-03 07:30:41

稍微盯着这个,我想我明白你要做什么了。每个级别实际上并没有不同,您只是解释了获得全部内容的最低级别。就像马特的评论所说,您需要单独声明回调。但您还需要将当前级别的列表传递到其中,以便您可以附加到正确的位置。我们可以尝试一种使用构造回调方​​法的方法,如下所示:

function Generator(e, level) {
    var list = e; // I think you need this for the closure to work, but my js is rusty so I may be wrong.

    return function() {
        var new_list = $('<ol></ol>');
        list.append(new_list); // you might want to make sure there's more than 0 siblings?

        $(this).siblings('h' + level).each(function(i) {
            the_id = $(this).attr('id');
            new_list.append('<li><a href="#' + the_id + '">' + $(this).text() + '</a></li>');
            if (level < 6) { // your max h tag level
                Generator(new_list, level + 1)();
            }
        });
    }
}

我不确定我是否正确实现了您想要做的事情,但想法是您可以继续创建子列表并将它们传回递归地进入函数。你最初的问题之一是你每次都必须让你的 jQuery 选择器更深。这种方法解决了这个问题。您可以简单地称呼它:

$('section').each(Generator($('nav'), 2)); // maybe not nav, something close to that

显然您的代码是从使用 $(this).find('h2') 而不是 $(this).siblings('h2') 开始的,因此该区域需要进行一些调整。但我确信这没什么大不了的。

我没有对此进行测试,所以我可能在某个地方至少犯了一个错误。

A bit of staring at this and I think I figured out what you're going for. Each level isn't actually different, you've just explained the minimum in levels to get it all. Like Matt's comment says, you need to declare the callbacks separately. But you also need to pass your current level of list into it, so you can append to the right place. We can try an approach that uses a method for constructing the callbacks, like this:

function Generator(e, level) {
    var list = e; // I think you need this for the closure to work, but my js is rusty so I may be wrong.

    return function() {
        var new_list = $('<ol></ol>');
        list.append(new_list); // you might want to make sure there's more than 0 siblings?

        $(this).siblings('h' + level).each(function(i) {
            the_id = $(this).attr('id');
            new_list.append('<li><a href="#' + the_id + '">' + $(this).text() + '</a></li>');
            if (level < 6) { // your max h tag level
                Generator(new_list, level + 1)();
            }
        });
    }
}

I'm not sure if I implemented the thing you're trying to do correctly, but the idea is that you can keep creating sub lists and pass them back into the function recursively. One of your original problems is that you have to make your jQuery selector deeper each time. This approach fixes that. You call it simply:

$('section').each(Generator($('nav'), 2)); // maybe not nav, something close to that

Obviously your code starts out by using $(this).find('h2') instead of $(this).siblings('h2'), so some adjustment is needed in that area. But I'm sure that's not a big deal.

I didn't test this, so I probably made at least one mistake somewhere.

苹果你个爱泡泡 2024-11-03 07:30:41

快速 Google 搜索显示此脚本;它是普通的 'ole javascript,但您应该能够根据您的需要调整它(或者,只是借用这个想法并编写您自己的代码)。

在尝试了一些之后,我认为 Juan 是对的:迭代方法似乎更容易。我组装了一个使用类似方法的快速 jQuery 插件:

(function($) {
    $.fn.buildTOC = function(options) {
        var opts = $.extend({
                scan: $(document) // By default, search the entire page for headings
            }, options),
            $toc = $(this),       // This is where we'll place our TOC links

            /*
            * Get the current level from the Heading tag.
            */
            getLevel = function(h) {
                return parseInt(h.substring(1));
            },

            /*
             * Creates a new sublist and returns it.
             * The randomly-generated ID just makes it easier to find the new list.
             */
            pushLevel = function(toc) {
                var id = 'node' + Math.round(Math.random() * 50000 + 1);
                toc.append($('<ol id="' + id + '"></ol>'));
                return $('#' + id, toc);
            },

            /*
             * Returns the last sublist containing an element at the current level;
             * otherwise, returns the parent list (for top-level items).
             */
            popLevel = function(toc, level) {
                var sub = $('.toc-level-' + level + ':last', toc);
                if (sub.length) {
                    return sub.parent();
                } else {
                    return $toc.children('ol');
                }
            },

            /*
             * Appends a link for the current tag to the current list.
             * Also adds a class for the current level (handy for styling), so it's easy
             * to find items at this level.
             */
            appendLink = function(toc, tag, level) {
                toc.append($('<li class="toc-level-' + level + '"><a href="#' + tag.id + '">' + tag.innerHTML + '</a></li>'))
            },

            buildTOC = function(toc) {
                var headings = $('h1,h2,h3,h4,h5,h6', opts.scan),
                    lastLevel = 0;
                for (var i=0, len=headings.length; i<len; i++) {
                    var currTag = headings[i],
                        currLevel = getLevel(currTag.tagName);
                    if (lastLevel == currLevel) {
                        // Siblings: just add a link for this item
                        appendLink(toc, currTag, currLevel);
                    } else if (lastLevel < currLevel) {
                        // Child: create a new list and append to that
                        toc = pushLevel(toc);
                        appendLink(toc, currTag, currLevel);
                    } else {
                        // Parent: move back out to the appropriate list
                        toc = popLevel(toc, currLevel);
                        appendLink(toc, currTag, currLevel);
                    }
                    lastLevel = currLevel;
                }
            };

        buildTOC($toc);
    };
})(jQuery);

您可以像这样使用它:

$(function() {
    $('#toc').buildTOC();
})

其中 toc 是链接所在容器的 ID。

A quick Google search revealed this script; it's plain 'ole javascript, but you should be able to adapt it to your needs (or, just borrow the idea and write your own code).

After playing around with this some, I think Juan is right: the iterative approach seems easier. I put together a quick jQuery plugin that uses a similar approach:

(function($) {
    $.fn.buildTOC = function(options) {
        var opts = $.extend({
                scan: $(document) // By default, search the entire page for headings
            }, options),
            $toc = $(this),       // This is where we'll place our TOC links

            /*
            * Get the current level from the Heading tag.
            */
            getLevel = function(h) {
                return parseInt(h.substring(1));
            },

            /*
             * Creates a new sublist and returns it.
             * The randomly-generated ID just makes it easier to find the new list.
             */
            pushLevel = function(toc) {
                var id = 'node' + Math.round(Math.random() * 50000 + 1);
                toc.append($('<ol id="' + id + '"></ol>'));
                return $('#' + id, toc);
            },

            /*
             * Returns the last sublist containing an element at the current level;
             * otherwise, returns the parent list (for top-level items).
             */
            popLevel = function(toc, level) {
                var sub = $('.toc-level-' + level + ':last', toc);
                if (sub.length) {
                    return sub.parent();
                } else {
                    return $toc.children('ol');
                }
            },

            /*
             * Appends a link for the current tag to the current list.
             * Also adds a class for the current level (handy for styling), so it's easy
             * to find items at this level.
             */
            appendLink = function(toc, tag, level) {
                toc.append($('<li class="toc-level-' + level + '"><a href="#' + tag.id + '">' + tag.innerHTML + '</a></li>'))
            },

            buildTOC = function(toc) {
                var headings = $('h1,h2,h3,h4,h5,h6', opts.scan),
                    lastLevel = 0;
                for (var i=0, len=headings.length; i<len; i++) {
                    var currTag = headings[i],
                        currLevel = getLevel(currTag.tagName);
                    if (lastLevel == currLevel) {
                        // Siblings: just add a link for this item
                        appendLink(toc, currTag, currLevel);
                    } else if (lastLevel < currLevel) {
                        // Child: create a new list and append to that
                        toc = pushLevel(toc);
                        appendLink(toc, currTag, currLevel);
                    } else {
                        // Parent: move back out to the appropriate list
                        toc = popLevel(toc, currLevel);
                        appendLink(toc, currTag, currLevel);
                    }
                    lastLevel = currLevel;
                }
            };

        buildTOC($toc);
    };
})(jQuery);

You'd use it like so:

$(function() {
    $('#toc').buildTOC();
})

Where toc is the ID of the container where the links should go.

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