7
votes

Comment analyser de manière robuste un document pour tout titre et construire un arbre
    de ces titres

Donc, j'utilise un document afin de saisir toutes les en-têtes avec empressement (). Je fais cela afin de créer une carte de document Microsoft Word Style avec Buildnav (). Cela fonctionne actuellement, mais ce n'est pas très robuste et ne casse à tout moment les rubriques ne suivent pas un ordre strict ... par exemple. (Si vous commencez par un H2, il se casse, si vous niez un H3 sous et H1, il se casse, etc.)

Je ne peux pas comprendre la meilleure façon de résoudre ce problème (le rendre plus robuste). Je tire parti de la fonction `NextUntil 'de JQuery pour trouver tous les H2S entre deux h1s. P>

Une possibilité remplace: p> xxx pré>

avec P>

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)


2 commentaires

Cela pourrait vous aider si vous attachez un exemple de certains des HTML que vous essayez d'analyser.


Il semble que vous ayez besoin de clarifier ce que vous voulez en dehors de cela. Voulez-vous respecter l'exactitude de sorte que seules H1 et H2 puissent suivre un H1, et que seuls H1, H2 ou H3 peuvent suivre H2; Voulez-vous appliquer qu'une balise HX ne peut pas être à l'intérieur d'une autre balise Hx? Ou voulez-vous juste faire de votre mieux pour honorer l'intention semblant du document que vous recevez?


3 Réponses :


3
votes

Vous pouvez simplement utiliser le jQuery toC plugin , il apparaît qu'ils sont le faire comme ceci: xxx

bien sûr , cela traite simplement tous les H1 , H2 et H3 de manière égale et crée la TOC en considérant la nidification et l'ordre de document des éléments uniquement. Mais n'est-ce pas que le comportement souhaité?

Il semblerait étrange d'avoir un H3 , qui est directement à l'intérieur d'un H1 , double en retrait dans le TOC. Si vous ne pouvez pas vivre avec cette incohérence, au lieu d'insérer le manquant H2 entre, je envisagerais de nettoyer le code HTML et de convertir le h3 en un H2 .


0 commentaires

6
votes

J'ai jeté ensemble de Javascript qui fera ce que vous voulez http://jsfiddle.net/fa4ew/

C'est une fonction récursive assez simple qui consomme un éventail d'éléments (nœuds) et crée la structure ultérieure en conséquence. Pour être cohérent avec la question que j'ajoute les éléments de la liste d'emplois (vides) lorsque vous à partir d'un H1 à un H3, etc. P>

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


5 commentaires

Je travaille sur la mise en œuvre de votre code, qui a l'air plutôt bien. Une chose que j'ai rencontrée est que les nœuds peuvent avoir des propriétés, ce qui semble mettre en extra "" "que cet algorithme ne peut pas gérer.


Ainsi, en essayant de comprendre votre exemple, je suis allé et j'ai essayé de le convertir en une solution davantage basée sur jQuery pouvant gérer »... Il y a quelque chose que je ne comprends pas la magie de la façon dont vous échangez ELM et LI, et le Éléments annexés que vous rassemblez en cours de route. Je mets votre exemple dans un autre violon pour démontrer la conversion. L'esprit lui donne un look et voyez si vous pouvez comprendre où le mien se vante? jsfiddle.net/funkyeah/s8m2t/3


J'ai aussi posté la question suivante que j'ai côtoyée ci-dessus, ici: Stackoverflow.com/Questtions/17451231/...


@funkyeah - jsfiddle.net/s8m2t/8 Votre bogue était la différence entre JQuery.append et Dom .appendcendchild. JQuery ne retourne pas l'enfant avec $ .append (enfant) Il renvoie la même liste afin que vous puissiez utiliser une chaînage. Le Dom.appendchild (enfant) renvoie l'enfant.


Oui, merci ... JQuery's AppendTo m'a aidé à résoudre ... j'ai fait la variante JQuery dans l'autre réponse en fonction de votre réponse



0
votes

Solution JQuery + Coffescript basée sur la réponse de @LastCoder http://jsfiddle.net/sysydky/1/ xxx

Utilisation xxx


0 commentaires