JavaScript DOM: Find Element Index In Container

JavascriptHtmlDomParent Child

Javascript Problem Overview


I need to find an index of element inside its container by object reference. Strangely, I cannot find an easy way. No jQuery please - only DOM.

UL
 LI
 LI
 LI - my index is 2
 LI

Yes, I could assign IDs to each element and loop through all nodes to match the ID but it seems a bad solution. Isn't there something nicer?

So, say I have an object reference to the third LI as in the example above. How do I know it is index 2?

Thanks.

Javascript Solutions


Solution 1 - Javascript

You could make usage of Array.prototype.indexOf. For that, we need to somewhat "cast" the HTMLNodeCollection into a true Array. For instance:

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

Then we could just call:

nodes.indexOf( liNodeReference );

Example:

<ul id="list">
    <li>foo</li>
    <li class="match">bar</li>
    <li>baz</li>    
</ul>

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

console.log( nodes.indexOf( liRef ) );

Solution 2 - Javascript

Here is how I do (2018 version ?) :

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

Tadaaaam. And, if ever you want to consider raw text nodes too, you can do this instead :

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

I spread the children into an array as they are an HTMLCollection (thus they do not work with indexOf).

Be careful that you are using Babel or that browser coverage is sufficient for what you need to achieve (thinkings about the spread operator which is basically an Array.from behind the scene).

Solution 3 - Javascript

2017 update

The original answer below assumes that the OP wants to include non-empty text node and other node types as well as elements. It doesn't seem clear to me now from the question whether this is a valid assumption.

Assuming instead you just want the element index, previousElementSibling is now well-supported (which was not the case in 2012) and is the obvious choice now. The following (which is the same as some other answers here) will work in everything major except IE <= 8.

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

Original answer

Just use previousSibling until you hit null. I'm assuming you want to ignore white space-only text nodes; if you want to filter other nodes then adjust accordingly.

function getNodeIndex(node) {
    var index = 0;
    while ( (node = node.previousSibling) ) {
        if (node.nodeType != 3 || !/^\s*$/.test(node.data)) {
            index++;
        }
    }
    return index;
}

Solution 4 - Javascript

Array.prototype.indexOf.call(this.parentElement.children, this);

Or use let statement.

Solution 5 - Javascript

For just elements this can be used to find the index of an element amongst it's sibling elements:

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

Note that previousElementSibling isn't supported in IE<9.

Solution 6 - Javascript

You can use this to find the index of an element:

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

This for example logs all indices:

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

JSFIDDLE

Solution 7 - Javascript

A modern native approach could make use of 'Array.from()' - for example: `

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>

`

Solution 8 - Javascript

An example of making an array from 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>

Solution 9 - Javascript

You can iterate through the <li>s in the <ul> and stop when you find the right one.

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

Solution 10 - Javascript

If you want to write this compactly all you need is:

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

We just cycle through the elements in the parent node, stopping when we find your element.

You can also easily do:

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

if that's all you want to look through.

Solution 11 - Javascript

    const nodes = Array.prototype.slice.call( el.parentNode.childNodes );
    const index = nodes.indexOf(el);
    console.log('index = ', index);

Solution 12 - Javascript

Another example just using a basic loop and index check

HTML

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

JavaScript runs onload/ready or after ul is rendered

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;
}

Running Example

jsFiddle

Solution 13 - Javascript

Just pass the object reference to the following function and you will get the 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);
				}
			}
		}
	}
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionVadView Question on Stackoverflow
Solution 1 - JavascriptjAndyView Answer on Stackoverflow
Solution 2 - JavascriptJulien BONNINView Answer on Stackoverflow
Solution 3 - JavascriptTim DownView Answer on Stackoverflow
Solution 4 - JavascriptAlexander AbashkinView Answer on Stackoverflow
Solution 5 - JavascriptWeb_DesignerView Answer on Stackoverflow
Solution 6 - Javascriptuser657496View Answer on Stackoverflow
Solution 7 - JavascriptcalipoopView Answer on Stackoverflow
Solution 8 - JavascriptberandomsenView Answer on Stackoverflow
Solution 9 - Javascriptgen_EricView Answer on Stackoverflow
Solution 10 - JavascriptAdaView Answer on Stackoverflow
Solution 11 - JavascriptNguyen Anh VuView Answer on Stackoverflow
Solution 12 - JavascriptepascarelloView Answer on Stackoverflow
Solution 13 - JavascriptClyde LoboView Answer on Stackoverflow