Question

Cela fait un moment que je réfléchis à cela, mais je ne peux pas trouver de solution efficace. Je n'arrive même pas à coder ça ...

Par exemple, vous avez une page avec une structure d'en-tête comme celle-ci:

<h1>Heading level 1</h1>

    <h2>Sub heading #1</h2>

    <h2>Sub heading #2</h2>

        <h3>Sub Sub heading</h3>

    <h2>Sub heading #3</h2>

        <h3>Sub Sub heading #1</h3>

        <h3>Sub Sub heading #2</h3>

            <h4>Sub Sub Sub heading</h4>

    <h2>Sub heading #4</h2>

        <h3>Sub Sub heading</h3>

En utilisant JavaScript (tout cadre convient), comment voulez-vous produire une liste comme celle-ci: (avec des listes imbriquées)

<ol>
    <li>Heading level 1
        <ol>
            <li>Sub heading #1</li>
            <li>Sub heading #2
                <ol>
                    <li>Sub Sub heading</li>
                </ol>
            </li>
            <li>Sub heading #3
                <ol>
                    <li>Sub Sub heading #1</li>
                    <li>Sub Sub heading #2
                        <ol>
                            <li>Sub Sub Sub heading (h4)</li>
                        </ol>
                    </li>
                </ol>
            </li>
            <li>Sub heading #4
                <ol>
                    <li>Sub Sub heading</li>
                </ol>
            </li>
        </ol>
    </li>
</ol>

Chaque fois que j'essaie de commencer avec une certaine méthodologie, cela finit par devenir très gonflé.

La solution doit parcourir chaque en-tête et la placer dans la liste imbriquée appropriée. Je ne cesse de me répéter, mais je ne peux rien esquisser!

Même si vous avez une méthodologie en tête mais que vous n'avez pas le temps de la coder, j'aimerais quand même la connaître! :)

Merci!

Était-ce utile?

La solution

Commencez par construire un arbre. Pseudocode (parce que je ne parle pas couramment Javascript):

var headings = array(...);
var treeLevels = array();
var treeRoots = array();

foreach(headings as heading) {
    if(heading.level == treeLevels.length) {
        /* Adjacent siblings. */

        if(heading.level == 1) {
            treeRoots[] = heading;  // Append.
        } else {
            treeLevels[treeLevels.length - 2].children[] = heading;  // Add child to parent element.
        }

        treeLevels[treeLevels.length - 1] = heading;
    } else if(heading.level > treeLevels.length) {
        /* Child. */

        while(heading.level - 1 > treeLevels.length) {
            /* Create dummy headings if needed. */
            treeLevels[] = new Heading();
        }

        treeLevels[] = heading;
    } else {
        /* Child of ancestor. */

        treeLevels.remove(heading.level, treeLevels.length - 1);

        treeLevels[treeLevels.length - 1].children[] = heading;
        treeLevels[] = heading;
    }
}

Ensuite, nous le traversons, en construisant la liste.

function buildList(root) {
    var li = new LI(root.text);

    if(root.children.length) {
        var subUl = new UL();
        li.children[] = subUl;

        foreach(root.children as child) {
            subUl.children[] = buildList(child);
        }
    }

    return li; 
}

Enfin, insérez le LI renvoyé par buildList dans un UL pour chaque treeRoots.

Dans jQuery, vous pouvez récupérer les éléments d'en-tête dans l'ordre indiqué:

var headers = $('*').filter(function() {
    return this.tagName.match(/h\d/i);
}).get();

Autres conseils

Le problème ici est qu’il n’existe aucun moyen efficace de récupérer les en-têtes dans l’ordre des documents. Par exemple, l'appel jQuery $('h1,h2,h3,h4,h5,h6') renverra toutes vos en-têtes, mais tous les <h1> s viendront en premier suivis des <h2> s, etc. Aucun travail de cadre majeur n'a encore été renvoyé dans l'ordre des documents lorsque vous utilisez des sélecteurs délimités par des virgules.

Vous pouvez résoudre ce problème en ajoutant une classe commune à chaque en-tête. Par exemple:

<h1 class="heading">Heading level 1</h1>

    <h2 class="heading">Sub heading #1</h2>

    <h2 class="heading">Sub heading #2</h2>

        <h3 class="heading">Sub Sub heading</h3>

    <h2 class="heading">Sub heading #3</h2>

    ...

Maintenant, le sélecteur $('.heading') les mettra tous en ordre.

Voici comment je le ferais avec jQuery:

var $result = $('<div/>');
var curDepth = 0;

$('h1,h2,h3,h4,h5,h6').addClass('heading');
$('.heading').each(function() {

    var $li = $('<li/>').text($(this).text());

    var depth = parseInt(this.tagName.substring(1));

    if(depth > curDepth) { // going deeper

        $result.append($('<ol/>').append($li));
        $result = $li;

    } else if (depth < curDepth) { // going shallower

        $result.parents('ol:eq(' + (curDepth - depth - 1) + ')').append($li);
        $result = $li;

    } else { // same level

        $result.parent().append($li);
        $result = $li;

    }

    curDepth = depth;

});

$result = $result.parents('ol:last');

// clean up
$('h1,h2,h3,h4,h5,h6').removeClass('heading');

$result devrait maintenant être votre <ol>.

Notez également que cela traitera un <h4> suivi d'un <=> (descendant de plus d'un niveau à la fois), mais pas un <=> suivi d'un <=> (plus d'un niveau à la fois).

Je peux imaginer de nombreuses situations où vous pourriez trop penser à cela. Dans de nombreuses situations, vous n'aurez besoin que de l'apparence de la hiérarchie, et non de la hiérarchie HTML régénérée elle-même, pour laquelle vous pouvez faire quelque chose de simple, comme ceci:

#nav li.h1 { padding: 0 0 0  0px; } #nav li.h1:before { content: 'h1 '; }
#nav li.h2 { padding: 0 0 0 10px; } #nav li.h2:before { content: 'h2 '; }
#nav li.h3 { padding: 0 0 0 20px; } #nav li.h3:before { content: 'h3 '; }
#nav li.h4 { padding: 0 0 0 30px; } #nav li.h4:before { content: 'h4 '; }
#nav li.h5 { padding: 0 0 0 40px; } #nav li.h5:before { content: 'h5 '; }
#nav li.h6 { padding: 0 0 0 50px; } #nav li.h6:before { content: 'h6 '; }

& nbsp;

for (i=1; i<=6; i++) {
    var headers = document.getElementsByTagName('h'+i);
    for (j=0; j<headers.length; j++) {
        headers[j].className = 'h';
    }
}
var headers = document.getElementsByClassName('h');
var h1 = document.getElementsByTagName('h1')[0];
h1.parentNode.insertBefore(document.createElement('ul'),h1.nextSibling);
h1.nextSibling.id = 'nav';
for (i=0; i<headers.length; i++) {
    document.getElementById('nav').innerHTML += '<li class="'+headers[i].tagName.toLowerCase()+'">'+headers[i].innerHTML+'</li>';
}

Ceci sélectionnera toutes les balises h1-h6 dans la section docElTgt DOM du document et sera respecté, les en-têtes de commande se trouvant dans le document html

var hItemsList = docElTgt.querySelectorAll('h1, h2, h3, h4, h5, h6');

exemples de la valeur qui peut être docElTgt:

docElTgt = document.body;
docElTgt = anyelement.id;

Après avoir sélectionné tous les en-têtes, il sera possible d'appliquer un algorithme pour faire de la hiérarchie une liste ordonnée comme le montrent d'autres utilisateurs

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top