En javascript pur (c'est-à-dire sans extensions telles que jQuery, etc.), existe-t-il un moyen de déterminer l'index d'un nœud enfant à l'intérieur de son nœud parent sans itérer et comparer tous les nœuds enfants?
Par exemple,
var child = document.getElementById('my_element');
var parent = child.parentNode;
var childNodes = parent.childNodes;
var count = childNodes.length;
var child_index;
for (var i = 0; i < count; ++i) {
if (child === childNodes[i]) {
child_index = i;
break;
}
}
Existe-t-il une meilleure façon de déterminer l'indice de l'enfant?
javascript
dom
Tous les travailleurs sont essentiels
la source
la source
parent.childNodes
, plutôt queparent.children
?. Ce dernier ne répertorie que lesElements
, en excluant en particulier lesText
nœuds ... Certaines des réponses ici, par exemple en utilisantpreviousSibling
, sont basées sur l'utilisation de tous les nœuds enfants, tandis que d'autres ne sont dérangées qu'avec des enfants qui sontElement
s ... (!).childNodes
devrait certainement être utilisé à la place.children
. Les 2 premières réponses publiées donneront des résultats différents comme vous l'avez souligné.Réponses:
vous pouvez utiliser la
previousSibling
propriété pour parcourir les frères et sœurs jusqu'à ce que vous revenieznull
et comptiez le nombre de frères que vous avez rencontrés:Veuillez noter que dans des langages comme Java, il existe une
getPreviousSibling()
fonction, mais dans JS, cela est devenu une propriété -previousSibling
.la source
for (var i=0; (node=node.previousSibling); i++);
i
sera donc accessible..previousSibling
et.previousElementSibling
. Le premier atteint les nœuds de texte, le second non.J'ai commencé à utiliser
indexOf
pour cela. Parce queindexOf
c'est alluméArray.prototype
etparent.children
est unNodeList
, vous devez utilisercall();
C'est un peu moche, mais c'est une ligne unique et utilise des fonctions que tout développeur javascript devrait connaître de toute façon.la source
[]
crée une instance Array chaque fois que vous exécutez ce code, ce qui est moins efficace pour la mémoire et GC que pour l'utilisationArray.prototype
.[]
nettoie- t-il pas la mémoire en tant que valeur de garbage?[].indexOf
le moteur, il faut créer une instance de tableau juste pour accéder à l'indexOf
implémentation sur le prototype. L'instance elle-même reste inutilisée (elle fait du GC, ce n'est pas une fuite, c'est juste une perte de cycles).Array.prototype.indexOf
accède directement à cette implémentation sans allouer d'instance anonyme. La différence sera négligeable dans presque toutes les circonstances, donc franchement, cela ne vaut peut-être pas la peine de s'en préoccuper.ES6:
Explication:
element.parentNode.children
→ Renvoie les frères deelement
, y compris cet élément.Array.from
→ Convertit le constructeur dechildren
enArray
objetindexOf
→ Vous pouvez postulerindexOf
car vous avez maintenant unArray
objet.la source
Array.from
travaux sur Internet Explorercreates a new Array instance from an array-like or iterable object.
Créer une nouvelle instance de tableau juste pour trouver un index peut ne pas être efficace en mémoire ou en GC, selon la fréquence de l'opération, auquel cas l'itération, comme expliqué dans la réponse acceptée, serait plus idéal.ES: plus court
L'opérateur de diffusion est un raccourci pour cela
la source
e.parentElement.childNodes
ete.parentNode.children
?childNodes
comprend également des nœuds de texteType 'NodeListOf<ChildNode>' must have a '[Symbol.iterator]()' method that returns an iterator.ts(2488)
Ajout d'un élément (préfixé pour la sécurité) .getParentIndex ():
la source
if (!Element.prototype.getParentIndex) Element.prototype.getParentIndex = function(){ /* code here */ }
? Quoi qu'il en soit, si cela est jamais implémenté dans la norme à l'avenir, il sera probablement implémenté comme un getter commeelement.parentIndex
. Donc, je dirais que la meilleure approche seraitif(!Element.prototype.getParentIndex) Element.prototype.getParentIndex=Element.prototype.parentIndex?function() {return this.parentIndex}:function() {return Array.prototype.indexOf.call(this.parentNode.children, this)}
getParentIndex()
peut avoir une signature différente de votre implémentation.Je suppose que, étant donné un élément où tous ses enfants sont ordonnés sur le document de manière séquentielle, le moyen le plus rapide devrait être de faire une recherche binaire, en comparant les positions des éléments dans le document. Cependant, comme introduit dans la conclusion, l'hypothèse est rejetée. Plus vous avez d'éléments, plus le potentiel de performance est grand. Par exemple, si vous aviez 256 éléments, alors (de manière optimale) vous n'auriez besoin de vérifier que 16 d'entre eux! Pour 65536, seulement 256! La performance atteint la puissance de 2! Voir plus de chiffres / statistiques. Visitez Wikipedia
Ensuite, la façon dont vous l'utilisez consiste à obtenir la propriété 'parentIndex' de n'importe quel élément. Par exemple, consultez la démo suivante.
Limites
Recherche Binary VS Linear Sur 200 mille éléments (peut planter certains navigateurs mobiles, ATTENTION!):
Recherche binaire
Afficher l'extrait de code
Recherche linéaire vers l'arrière (`lastIndexOf`)
Afficher l'extrait de code
Recherche linéaire avant (`indexOf`)
Afficher l'extrait de code
PrécédentElementSibling Counter Search
Compte le nombre de PreviousElementSiblings pour obtenir le parentIndex.
Afficher l'extrait de code
Pas de recherche
Pour évaluer ce que serait le résultat du test si le navigateur optimisait la recherche.
Afficher l'extrait de code
La Conculsion
Cependant, après avoir consulté les résultats dans Chrome, les résultats sont à l'opposé de ce qui était attendu. La recherche linéaire plus stupide vers l'avant était une surprenante 187 ms, 3850%, plus rapide que la recherche binaire. De toute évidence, Chrome a en quelque sorte déjoué
console.assert
et optimisé par magie , ou (plus optimiste) Chrome utilise en interne un système d'indexation numérique pour le DOM, et ce système d'indexation interne est exposé à travers les optimisations appliquéesArray.prototype.indexOf
lorsqu'il est utilisé sur unHTMLCollection
objet.la source
Utilisez un algorithme de recherche binaire pour améliorer les performances lorsque le nœud a des frères et sœurs en grande quantité.
la source
J'ai eu un problème avec les nœuds de texte et l'index était incorrect. Voici la version pour y remédier.
la source
Ex
la source
Element.prototype
? Les fonctions semblent utiles mais je ne sais pas ce que font ces fonctions (même si les noms sont évidents).el.group.value()
??. Mon premier commentaire est là pour améliorer la qualité de votre réponse.Pourriez-vous faire quelque chose comme ça:
https://developer.mozilla.org/en-US/docs/Web/API/Node/parentElement
la source
la source