61

I want find the index of a given DOM node. It's like the inverse of doing

document.getElementById('id_of_element').childNodes[K]

I want to instead extract the value of K given that I already have the reference to the child node and the parent node. How do I do this?

7 Answers 7

105

The shortest possible way, without any frameworks, in all versions of Safari, FireFox, Chrome and IE >= 9:

var i = Array.prototype.indexOf.call(e.childNodes, someChildEl);

Sign up to request clarification or add additional context in comments.

11 Comments

Note: replace e.childNodes with e.children to find the index of an element within its sibling elements - as opposed to sibling nodes, which include text and comment nodes.
Why can't we do something like e.parentNode.childNodes.indexOf(e)? Edit: I just tested it. NodeList is not a real Array so we have to pull in Array's impl of indexOf through its prototype. It is surprising to me that Array's indexOf functions on NodeLists and whether this is intended or coincidental.
@StevenLu Your first question: indexOf on a NodeList is not defined in the DOM spec, not even in DOM4. Your last point: it's intended. Array methods are generic enough to function on almost anything with a length property.
@StevenLu It is intentional. See ECMA-262 Edition 5 page 244 (145 in the pdf), the last note of section 15.4.4.14: "The indexOf function is intentionally generic; it does not require that its this value be an Array object. Therefore it can be transferred to other kinds of objects for use as a method. Whether the indexOf function can be applied successfully to a host object is implementation-dependent."
At the expense of an array allocation it can be shortened to [].indexOf.call(children, el), unless that doesn't hold with the browser compatibility statement (I'd find that surprising though).
|
23

A little shorter, expects the element to be in elem, returns k.

for (var k=0,e=elem; e = e.previousSibling; ++k);

After a comment from Justin Dearing I reviewed my answer and added the following:

Or if you prefer "while":

var k=0, e=elem;
while (e = e.previousSibling) { ++k;}

The original question was how to find the index of an existing DOM element. Both of my examples above in this answer expects elem to be an DOM element and that the element still exists in the DOM. They will fail if you give them an null object or an object that don't have previousSibling. A more fool-proof way would be something like this:

var k=-1, e=elem;
while (e) {
    if ( "previousSibling" in e ) {
        e = e.previousSibling;
        k = k + 1;
    } else {
        k= -1;
        break;
    }
}   

If e is null or if previousSibling is missing in one of the objects, k is -1.

12 Comments

@some, I had to modify the example, and it looks like you have to move e = e.previousSibling to the miffle of the for loop and do a null check in firefox 3.5.
@Justin: "e=e.previousSibling" is in the middle... The for-loop declares k=0 and e=elem. Then it execute e=e.previousSibling until e===null. For every successful loop it increments k by one. And it works in FF3.5.2. Please explain what you think is wrong.
@Justin: Added some more examples.
some - If I may suggest, as above/below are relative specifiers, that you might consider inserting "Both of the examples above (1 & 2) expects elem" in place of the similar text in your answer, so if ever relevant, someone knows which two previous examples (assuming I am right) you are talking about.
@brunoais In the for example you are using previousElementSibling and get the result 24, but in the while you are using previousSibling with the result of 49. I created a new testcase where they both use previousElementSibling and then there isn't that much of a difference anymore in my limited testing at least. Actually the while was a little faster in FF20 on Win7.
|
4

RoBorg's answer works... or you could try...

var k = 0;
while(elem.previousSibling){
    k++;
    elem = elem.previousSibling;
}
alert('I am at index: ' + k);

1 Comment

Can't you replace while(elem.previousSibling){k++;elem = elem.previousSibling;} with while(elem=elem.previousSibling){k++;}?
3

A modern native approach might include Array.from(e.children).indexOf(theChild)

No IE support, but Edge works: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from

Comments

1

As with the original poster, I was trying to

find the index of a given DOM node

but one that I had just use a click handler on, and only in relation to its siblings. I couldn't end up getting the above to work (because of noobness undoubtably, i tried subbing in 'this' for elem but it didn't work).

My solution was to use jquery and use:

var index = $(this).parent().children().index(this);

It works without having to specify the type of the element ie:'h1' or an id etc.

Comments

0

I think the only way to do this is to loop through the parent's children until you find yourself.

var K = -1;
for (var i = myNode.parent.childNodes.length; i >= 0; i--)
{
    if (myNode.parent.childNodes[i] === myNode)
    {
        K = i;
        break;
    }
}

if (K == -1)
    alert('Not found?!');

Comments

-1

using a framework like prototype you could use this :

$(el).up().childElements().indexOf($(el))

Comments