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

时间:2011-03-24 21:21:33

标签: javascript jquery html recursion

我有一些代码:

$('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 。我怎样才能编写一个这样做的递归函数?

3 个答案:

答案 0 :(得分:2)

构建列表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') ) );

我在你的页面上运行了它,它复制了你现有的TOC。并且您不需要每个级别的自定义代码;又快又脏。

答案 1 :(得分:0)

有点盯着这个,我想我想出了你想要的东西。每个级别实际上并没有不同,你刚刚解释了所有级别的最低级别。就像Matt的评论所说,你需要单独声明回调。但是您还需要将当前级别的列表传递给它,这样您就可以将其附加到正确的位置。我们可以尝试一种使用方法来构造回调的方法,如下所示:

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')开始的,因此需要在该区域进行一些调整。但我相信这不是什么大问题。

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

答案 2 :(得分:0)

quick Google搜索显示this script;它很简单,但你应该能够根据自己的需要进行调整(或者只是借用这个想法并编写自己的代码)。

在玩了一些之后,我认为胡安是对的:迭代方法似乎更容易。我整理了一个使用类似方法的快速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。