JavaScript DOM: Elementindex im Container suchen

74

Ich muss einen Index des Elements in seinem Container durch Objektreferenz finden. Seltsamerweise kann ich keinen einfachen Weg finden. Bitte keine jQuery - nur DOM.

UL
 LI
 LI
 LI - my index is 2
 LI

Ja, ich könnte jedem Element IDs zuweisen und alle Knoten durchlaufen, um mit der ID übereinzustimmen, aber es scheint eine schlechte Lösung zu sein. Gibt es nicht etwas Schöneres?

Angenommen, ich habe einen Objektverweis auf den dritten LI wie im obigen Beispiel. Woher weiß ich, dass es Index 2 ist?

Vielen Dank.

Vad
quelle
Wie müssen Sie den Index erhalten? schweben .. etc ??
Clyde Lobo
1
Warum nicht ein vorheriges Geschwister für die li-Referenz ausführen, bis Sie null drücken?
Ashwin Prabhu
Mögliches Duplikat des Finding DOM Node Index
Dan Dascalescu
Ich denke, es wäre einfach, wenn Sie dem li-Element ein benutzerdefiniertes Attribut hinzufügen. Beispiel: <li index = "0">, <li index = "1"> und Sie können problemlos darauf zugreifen.
HENG Vongkol

Antworten:

89

Sie könnten Gebrauch machen von Array.prototype.indexOf. Dafür müssen wir das etwas HTMLNodeCollectionin ein wahres "verwandeln" Array. Zum Beispiel:

var nodes = Array.prototype.slice.call( document.getElementById('list').children );

Dann könnten wir einfach anrufen:

nodes.indexOf( liNodeReference );

Beispiel:

var nodes = Array.prototype.slice.call( document.getElementById('list').children ),
    liRef = document.getElementsByClassName('match')[0];

console.log( nodes.indexOf( liRef ) );
<ul id="list">
    <li>foo</li>
    <li class="match">bar</li>
    <li>baz</li>    
</ul>

jAndy
quelle
3
Hinweis: .childNodesund .childrensind verschiedene Dinge. .childrenist eine Teilmenge, nur eine Liste aller ElementNodes.
Ian Clark
2
Die previousElementSiblingLösung ist besser, da sie Textknoten ausschließt und den Index des erwarteten Elements zurückgibt.
Dan Dascalescu
3
ES6-Syntax:const index = [...el.parentNode.children].indexOf(el)
ESR
Ich würde es Array.from( el.parentNode.children ).indexOf( el );in diesem Fall eigentlich sehr bevorzugen , nur um die Lesbarkeit zu gewährleisten.
jAndy
51

Update 2017

Die ursprüngliche Antwort unten geht davon aus, dass das OP nicht leere Textknoten und andere Knotentypen sowie Elemente enthalten möchte. Aus der Frage, ob dies eine gültige Annahme ist, scheint mir jetzt nicht klar zu sein.

Angenommen, Sie möchten stattdessen nur den Elementindex, previousElementSiblingwird jetzt gut unterstützt (was 2012 nicht der Fall war) und ist jetzt die offensichtliche Wahl. Das Folgende (was mit einigen anderen Antworten hier identisch ist) funktioniert in allen wichtigen Bereichen außer IE <= 8.

function getElementIndex(node) {
    var index = 0;
    while ( (node = node.previousElementSibling) ) {
        index++;
    }
    return index;
}

Ursprüngliche Antwort

Verwenden previousSiblingSie einfach, bis Sie treffen null. Ich gehe davon aus, dass Sie nur Leerzeichen-Textknoten ignorieren möchten. Wenn Sie andere Knoten filtern möchten, passen Sie sie entsprechend an.

function getNodeIndex(node) {
    var index = 0;
    while ( (node = node.previousSibling) ) {
        if (node.nodeType != 3 || !/^\s*$/.test(node.data)) {
            index++;
        }
    }
    return index;
}
Tim Down
quelle
6
+1 Dies ist im Vergleich zu jeder anderen hier beschriebenen Methode am schnellsten. Für neuere Browser kann previousElementSibling ohne diese beiden Bedingungen verwendet werden.
Akash Kava
@ bjb568: Ist Ihr Punkt, dass es eine Reihe von unnötigen Klammern gibt?
Tim Down
2
Nicht unnötig. Es ist nicht genug. Jeder weiß, dass Sie mindestens weitere 7 für Schönheit brauchen.
bjb568
@ bjb568: Wahrscheinlich am besten, um sie auch zu schließen.
Tim Down
3
Was macht dieser! / ^ \ s * $ /. Test (node.data) genau?
Maxlego
45

So mache ich das (Version 2018?) :

const index = [...el.parentElement.children].indexOf(el);

Tadaaaam. Und wenn Sie jemals auch Rohtextknoten berücksichtigen möchten, können Sie dies stattdessen tun:

const index = [...el.parentElement.childNodes].indexOf(el);

Ich verteile die Kinder in einem Array, da sie eine HTMLCollection sind (daher funktionieren sie nicht mit indexOf).

Achten Sie darauf, dass Sie Babel verwenden oder dass die Browserabdeckung für das, was Sie erreichen möchten, ausreicht (Überlegungen zum Spread-Operator, der im Grunde genommen ein Array.from hinter den Kulissen ist).

Julien BONNIN
quelle
Kann bestätigen, dass dies funktioniert. Sie müssen nichts schleifen.
Fabian von Ellerts
Beste Antwort IMO, nett und prägnant
Aaria Carter-Weir
1
Wirklich cool! Gerade Array.fromzu den childNodes hinzugefügtconst index = [...Array.from(el.parentElement.children)].indexOf(el);
David Dal Busco
Bei Verwendung als Teil eines Interviews sollten Sie erwähnen, dass Array.from (oder [...]) eine flache Kopie der Sammlung erstellt, während Array.prototype.indexOf.call dies nicht tut.
CWright
19
Array.prototype.indexOf.call(this.parentElement.children, this);

Oder verwenden Sie die letAnweisung.

Alexander Abashkin
quelle
@citykid, sicher, es ist knapper, aber es ist albern, ein temporäres Array zu erstellen, nur um auf eine Funktion dieses Arrays zuzugreifen.
Gman
10

Für nur Elemente kann dies verwendet werden, um den Index eines Elements unter seinen Geschwisterelementen zu finden:

function getElIndex(el) {
    for (var i = 0; el = el.previousElementSibling; i++);
    return i;
}

Beachten Sie, dass previousElementSiblingdies in IE <9 nicht unterstützt wird.

Webdesigner
quelle
1
Was ist, wenn es kein vorheriges Geschwister ist, sondern das nächste?
Rootical V.
@RooticalV.: Nicht sicher, was du meinst. Wollten Sie fragen, was passiert, wenn es kein vorheriges Geschwister gibt? In diesem Fall wird die Schleife automatisch beendet, da der Wert des Ausdrucks "el = el.previousElementSibling" falsch ist (da er null ist). Oder wollten Sie fragen, was passieren würde, wenn Sie "previousElementSibling" im obigen Code durch "nextElementSibling" ersetzen würden? In diesem Fall würde die Schleife die Anzahl der Geschwister zählen, die dem Element noch folgen.
HammerNL
8

Sie können dies verwenden, um den Index eines Elements zu finden:

Array.prototype.indexOf.call(yourUl, yourLi)

Dies protokolliert zum Beispiel alle Indizes:

var lis = yourList.getElementsByTagName('li');
for(var i = 0; i < lis.length; i++) {
    console.log(Array.prototype.indexOf.call(lis, lis[i]));
}​

JSFIDDLE

rsplak
quelle
Ich habe die Frage falsch interpretiert. Bearbeitet!
Rsplak
Die previousElementSiblingLösung ist besser, da sie Textknoten ausschließt und den Index des erwarteten Elements zurückgibt.
Dan Dascalescu
3

Ein moderner nativer Ansatz könnte 'Array.from ()' verwenden - zum Beispiel: `

const el = document.getElementById('get-this-index')
const index = Array.from(document.querySelectorAll('li')).indexOf(el)
document.querySelector('h2').textContent = `index = ${index}`
<ul>
  <li>zero
  <li>one
  <li id='get-this-index'>two
  <li>three
</ul>
<h2></h2>

`

Calipoop
quelle
2

Ein Beispiel für die Erstellung eines Arrays aus HTMLCollection

<ul id="myList">
    <li>0</li>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
</ul>

<script>
var tagList = [];

var ulList = document.getElementById("myList");

var tags   = ulList.getElementsByTagName("li");

//Dump elements into Array
while( tagList.length != tags.length){
    tagList.push(tags[tagList.length])
};

tagList.forEach(function(item){
        item.addEventListener("click", function (event){ 
            console.log(tagList.indexOf( event.target || event.srcElement));
        });
}); 
</script>
berandomsen
quelle
1

Wenn Sie dies kompakt schreiben möchten, brauchen Sie nur:

var i = 0;
for (;yourElement.parentNode[i]!==yourElement;i++){}
indexOfYourElement = i;

Wir durchlaufen einfach die Elemente im übergeordneten Knoten und halten an, wenn wir Ihr Element finden.

Sie können auch leicht tun:

for (;yourElement.parentNode.getElementsByTagName("li")[i]!==yourElement;i++){}

wenn das alles ist, was du durchschauen willst.

Ada
quelle
1

Sie können das <li>s im durchlaufen <ul>und anhalten, wenn Sie das richtige gefunden haben.

function getIndex(li) {
    var lis = li.parentNode.getElementsByTagName('li');
    for (var i = 0, len = lis.length; i < len; i++) {
        if (li === lis[i]) {
            return i;
        }
    }

}

Demo

Rakete Hazmat
quelle
1
    const nodes = Array.prototype.slice.call( el.parentNode.childNodes );
    const index = nodes.indexOf(el);
    console.log('index = ', index);
Nguyen Anh Vu
quelle
0

Ein weiteres Beispiel, bei dem nur eine grundlegende Schleifen- und Indexprüfung verwendet wird

HTML

<ul id="foo">
    <li>0</li>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
</ul>

JavaScript wird onload / ready ausgeführt oder nachdem ul gerendert wurde

var list = document.getElementById("foo"),
    items = list.getElementsByTagName("li");

list.onclick = function(e) {
    var evt = e || window.event,
    src = evt.target || evt.srcElement;
    var myIndex = findIndex(src);
    alert(myIndex);
};

function findIndex( elem ) {
    var i, len = items.length;
    for(i=0; i<len; i++) {
        if (items[i]===elem) {
            return i;
        }
    }
    return -1;
}

Laufendes Beispiel

jsFiddle

Epascarello
quelle
0

Übergeben Sie einfach die Objektreferenz an die folgende Funktion und Sie erhalten den Index

function thisindex(elm) 
{ 
    var the_li = elm; 
    var the_ul = elm.parentNode; 
    var li_list = the_ul.childNodes; 

    var count = 0; // Tracks the index of LI nodes

    // Step through all the child nodes of the UL
    for( var i = 0; i < li_list.length; i++ )
    {
        var node = li_list.item(i);
        if( node )
        {
        // Check to see if the node is a LI
            if( node.nodeName == "LI" )
            {
            // Increment the count of LI nodes
                count++;
            // Check to see if this node is the one passed in
                if( the_li == node )
                {
                    // If so, alert the current count
                    alert(count-1);
                }
            }
        }
    }
}
Clyde Lobo
quelle