Obtenir l'index de l'élément en tant qu'enfant par rapport au parent

160

Disons que j'ai ce balisage:

<ul id="wizard">
    <li>Step 1</li>
    <li>Step 2</li>
</ul>

Et j'ai ce jQuery:

$("#wizard li").click(function () {
    // alert index of li relative to ul parent
});

Comment puis-je obtenir l'index de l'enfant par lirapport à son parent, en cliquant dessus li?

Par exemple, lorsque vous cliquez sur «Étape 1», un alertavec «0» devrait apparaître.

AgileMeansDoAsLittleAsPossible
la source

Réponses:

247
$("#wizard li").click(function () {
    console.log( $(this).index() );
});

Cependant, plutôt que d'attacher un gestionnaire de clic pour chaque élément de liste, il est préférable (en termes de performances) d'utiliser delegatece qui ressemblerait à ceci:

$("#wizard").delegate('li', 'click', function () {
    console.log( $(this).index() );
});

Dans jQuery 1.7+, vous devez utiliser on. L'exemple ci-dessous lie l'événement à l' #wizardélément, fonctionnant comme un événement délégué:

$("#wizard").on("click", "li", function() {
    console.log( $(this).index() );
});
carré rouge
la source
3
salut redsquare .. Je m'entraîne avec jQuery, je suis un peu n00b à propos de délégué: D .. pourquoi déléguer est-il mieux que d'attacher des événements directement sur les éléments?
stecb
1
@steweb - Hey - car un gestionnaire d'événements est de loin préférable à plusieurs. Attacher des événements au dom peut être coûteux si vous avez de grandes listes, donc en règle générale, j'essaie d'utiliser ce qui précède lorsque cela est possible plutôt que des gestionnaires individuels sur chaque élément. Un article qui va plus loin est briancrescimanno.com/2008/05/19 / ... - également google 'délégation d'événements javascript'
redsquare
ok, donc, gérer les événements de cette façon est quelque chose de plus léger que le classique 'dom attachment' :) .. merci!
stecb
@steweb - Il est également plus léger car jquery n'a pas besoin de faire une recherche initiale de tous les éléments li. Il recherche simplement le conteneur et écoute à ce niveau pour voir si l'objet cliqué était un li.
redsquare
Cette méthode .index m'est très utile. J'utilise jQuery Sortable pour faire le tri, mais il stocke les informations de classement sémantique dans le DOM. Avec .index (), je peux extraire le classement du dom vraiment proprement. Merci!
SimplGy
41

quelque chose comme:

$("ul#wizard li").click(function () {
  var index = $("ul#wizard li").index(this);
  alert("index is: " + index)
});
Purée
la source
9
Excellent ajout - nous pouvons trouver l'index par rapport au sélecteur parent. +1
BasTaller
1
préférez cette réponse car elle répond en fait à la question plutôt qu'à une solution possible pour l'exemple
DarkMukke
8

Jetez un œil à cet exemple .

$("#wizard li").click(function () {
    alert($(this).index()); // alert index of li relative to ul parent
});
Kimtho6
la source
4

Il n'est pas nécessaire d'avoir besoin d'une grande bibliothèque comme jQuery pour accomplir cela, si vous ne le souhaitez pas. Pour y parvenir avec la manipulation DOM intégrée, obtenez une collection des lifrères et sœurs dans un tableau et, en cliquant, vérifiez l' indexOfélément cliqué dans ce tableau.

const lis = [...document.querySelectorAll('#wizard > li')];
lis.forEach((li) => {
  li.addEventListener('click', () => {
    const index = lis.indexOf(li);
    console.log(index);
  });
});
<ul id="wizard">
    <li>Step 1</li>
    <li>Step 2</li>
</ul>

Ou, avec délégation événementielle:

const lis = [...document.querySelectorAll('#wizard li')];
document.querySelector('#wizard').addEventListener('click', ({ target }) => {
  // Make sure the clicked element is a <li> which is a child of wizard:
  if (!target.matches('#wizard > li')) return;
  
  const index = lis.indexOf(target);
  console.log(index);
});
<ul id="wizard">
    <li>Step 1</li>
    <li>Step 2</li>
</ul>

Ou, si les éléments enfants peuvent changer dynamiquement (comme avec une liste de tâches), vous devrez alors construire le tableau de lis à chaque clic, plutôt qu'au préalable:

const wizard = document.querySelector('#wizard');
wizard.addEventListener('click', ({ target }) => {
  // Make sure the clicked element is a <li>
  if (!target.matches('li')) return;
  
  const lis = [...wizard.children];
  const index = lis.indexOf(target);
  console.log(index);
});
<ul id="wizard">
    <li>Step 1</li>
    <li>Step 2</li>
</ul>

CertainPerformance
la source
$ (...). indexOf n'est pas une fonction
Kareem
2
Aucun des extraits de code de ma réponse ne produit cette erreur - vous pouvez appuyer sur "Exécuter l'extrait de code" pour les voir fonctionner. Si vous obtenez cette erreur, vous utilisez un code différent - on dirait que vous utilisez jQuery, mais ma réponse montre comment accomplir ce genre de chose sans jQuery
CertainPerformance
C'est suffisant. Oui, j'utilise JQuery. Merci
Kareem
3

Delegate et Live sont faciles à utiliser mais si vous n'avez plus de li: s ajoutés dynamiquement, vous pouvez également utiliser la délagation d'événement avec un lien / clic normal. Il devrait y avoir un gain de performances en utilisant cette méthode car le DOM n'aura pas à être surveillé pour de nouveaux éléments correspondants. Je n'ai pas de chiffres réels mais c'est logique :)

$("#wizard").click(function (e) {
    var source = $(e.target);
    if(source.is("li")){
        // alert index of li relative to ul parent
        alert(source.index());
    }
});

Vous pouvez le tester sur jsFiddle: http://jsfiddle.net/jimmysv/4Sfdh/1/

jimmystormig
la source
1
À quoi faites-vous référence lorsque vous dites que "DOM n'aura pas à être surveillé"? Le délégué jq ne surveille pas le dom.
redsquare
@redsquare J'avais l'impression que Delegate n'est qu'une variante plus efficace de Live. Ceci provient de api.jquery.com/delegate : "Attachez un gestionnaire à un ou plusieurs événements pour tous les éléments qui correspondent au sélecteur, maintenant ou dans le futur, en fonction d'un ensemble spécifique d'éléments racine." Il faut surveiller pour se lier à l'avenir?
jimmystormig
non, il utilise simplement la délégation sur le conteneur comme votre code ci-dessus. Si vous insérez un nouvel élément li dans le conteneur, l'événement de clic sur le conteneur est toujours déclenché et tout fonctionne toujours. Aucune surveillance requise.
redsquare
@redsquare Oui, vous avez raison. Ma solution fonctionne même s'il y a de nouveaux éléments ajoutés après le chargement du DOM. Puisque Delegate utilise Live en interne (voir stackoverflow.com/questions/2954932/… ), on a toujours l'impression que c'est plus optimisé mais moins pratique. Mais comme je l'ai dit, je n'ai pas de chiffres.
jimmystormig
2

Encore une autre façon

$("#wizard li").click(function () 
{
    $($(this),'#wizard"').index();
});

Démo https://jsfiddle.net/m9xge3f5/

h0mayun
la source