7

因此,我解析了一个文档,以便使用 stackHeadings() 获取所有标题。我这样做是为了使用 buildNav() 构建 Microsoft Word 样式的文档地图。这目前工作正常,但它不是很健壮,并且在标题不遵循严格顺序的任何时候都会中断......例如(如果你从 H2 开始它会中断,如果你在下面嵌套 H3 并且 H1 它会中断,等等......)

我无法弄清楚解决此问题的最佳方法(使其更强大)。我正在利用 jQuery 的 `nextUntil' 函数来查找两个 h1 之间的所有 h2。

一种可能性是替换:

elem.nextUntil( 'h' + cur, 'h' + next )

elem.nextUntil( 'h' + cur, 'h' + next + ',h' + (next + 1) + ',h' + (next + 2) ... )

查找同一级别的两个标题之间的所有子标题。但是现在 h1s 的 h3 孩子只会嵌套一层而不是两层。

因此,您必须将当前标题级别与父标题级别进行比较,如果跳跃超过一个(h1 -> h3),您必须在它们之间创建一个空子作为嵌套占位符失踪的h2。

任何想法或解决方案将不胜感激!

stackHeadings = (items, cur, counter) ->

    cur = 1 if cur == undefined
    counter ?= 1
    next = cur + 1
    for elem, index in items
      elem = $(elem)
      children  =  filterHeadlines( elem.nextUntil( 'h' + cur, 'h' + next ) )
      d.children = stackHeadings( children, next, counter ) if children.length > 0
      d


filterHeadlines = ( $hs ) ->
    _.filter( $hs, ( h ) -> $(h).text().match(/[^\s]/) )

buildNav = ( ul, items ) ->
    for child, index in items
        li = $( "<li>" )
        $( ul ).append( li )
        $a = $("<a/>")
        $a.attr( "id", "nav-title-" + child.id )

        li.append( $a )

        if child.children
            subUl = document.createElement( 'ul' )
            li.append( subUl )
            buildNav( subUl, child.children )

items = stackHeadings( filterHeadlines( source.find( 'h1' ) ) )
ul = $('<ul>')
buildNav( ul, items)
4

3 回答 3

6

我把一些 JavaScript 拼凑在一起,可以做你想做的事http://jsfiddle.net/fA4EW/

这是一个相当简单的递归函数,它使用一组元素(节点)并相应地构建 UL 结构。为了与问题保持一致,当您从 H1 到 H3 等时,我添加了占位符(空)列表元素。

function buildRec(nodes, elm, lv) {
    var node;
    // filter
    do {
        node = nodes.shift();
    } while(node && !(/^h[123456]$/i.test(node.tagName)));
    // process the next node
    if(node) {
        var ul, li, cnt;
        var curLv = parseInt(node.tagName.substring(1));
        if(curLv == lv) { // same level append an il
            cnt = 0;
        } else if(curLv < lv) { // walk up then append il
            cnt = 0;
            do {
                elm = elm.parentNode.parentNode;
                cnt--;
            } while(cnt > (curLv - lv));
        } else if(curLv > lv) { // create children then append il
            cnt = 0;
            do {
                li = elm.lastChild;
                if(li == null)
                    li = elm.appendChild(document.createElement("li"));
                elm = li.appendChild(document.createElement("ul"));
                cnt++;
            } while(cnt < (curLv - lv));
        }
        li = elm.appendChild(document.createElement("li"));
        // replace the next line with archor tags or whatever you want
        li.innerHTML = node.innerHTML;
        // recursive call
        buildRec(nodes, elm, lv + cnt);
    }
}
// example usage
var all = document.getElementById("content").getElementsByTagName("*");
var nodes = []; 
for(var i = all.length; i--; nodes.unshift(all[i]));
var result = document.createElement("ul");
buildRec(nodes, result, 1);
document.getElementById("outp").appendChild(result);
于 2013-07-02T16:10:38.563 回答
3

您可以简单地使用jQuery TOC 插件,看起来他们正在这样做

$('h1,h2,h3').each(function(i, heading) {
    ...
}

当然,这只是简单地对待 all和平等h1,并通过仅考虑元素的嵌套和文档顺序来创建 TOC。但这不是理想的行为吗?h2h3

h3在 TOC 中有一个直接在 a 内的h1双缩进的看起来会很奇怪。如果您不能忍受这种不一致,h2我会考虑清理 HTML 并将其转换h3h2.

于 2013-06-29T16:56:09.003 回答
0

基于@LastCoder 的回答 http://jsfiddle.net/Sydky/1/的Jquery+Coffescript 解决方案

buildRec = (headingNodes, $elm, lv) ->
  # each time through recursive function pull a piece of the jQuery object off
  node = headingNodes.splice(0,1)
  if node && node.length > 0
    curLv = parseInt(node[0].tagName.substring(1))
    if curLv is lv # same level append an il
        cnt = 0
    else if curLv < lv # walk up then append il
        cnt = 0
        loop
            $elm = $elm.parent().parent()
            cnt--
            break unless cnt > (curLv - lv)
    else if curLv > lv # create children then append li
        cnt = 0
        loop
            li = $elm.children().last() # if there are already li's at this level
            if ($elm.children().last().length == 0)
                li = $("<li>").appendTo($elm);
            $elm = $("<ul>").appendTo(li);
            cnt++
            break unless cnt < (curLv - lv)
    li = $("<li>").appendTo($elm);
    li.text(node[0].innerText);
    # recursive call
    buildRec headingNodes, $elm, lv + cnt

用法

headingNodes = $('#entry').children().filter(":header")
result = $('<ul>')
buildRec(headingNodes,result,1)
result.html()
于 2013-07-03T18:23:32.090 回答