Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

classList.remove is removing elements from a HTMLCollection?

Tags:

javascript

I'm encountering some very strange behaviour with JavaScripts new classList API, say we have the following HTML code:

<p class="testing">Lorem Ipsum</p>
<p class="testing">Lorem Ipsum</p>

And the following JavaScript code:

var elements = document.getElementsByClassName("testing");
alert(elements.length);
elements[0].classList.remove("testing");
alert(elements.length);

The first alert will give you a value of 2, whilst the second alert returns 1.

It appears that removing the class from the element is also removing it from the elements HTMLCollection, which makes absolutely no sense to me.

You can see an example of this code HERE.

I encountered this problem when trying to remove a certain class from some elements using code like below:

var elements = document.getElementsByClassName('testing');
var elementsLength = elements.length - 1;
for(var i = 0; i <= elementsLength ; i++)
{
    elements[i].classList.remove('testing');
}

Say we have two elements like in the example above, the loop runs successfully the first time, but the second time it's looking for an element in the HTMLCollection that no longer exists, so I get something like "TypeError: elements[i] is undefined".

You can see an example of the above code HERE

This is frustrating to say the least, I can't understand why/how classList.remove could effect what is effectively an array set only once before the classList.remove function is called. I can't even seem to find anything about this behaviour online.

Am I doing something crazy? Or has I unearthed some strange hidden feature of the classList api that no one knows about?

like image 764
Sean Avatar asked Mar 01 '14 00:03

Sean


2 Answers

The collection returned by document.getElementsByClassName is live so if an element doesn't have that class anymore it will be removed from the collection.

You can either create a non-live copy of the collection:

var elements = [].slice.call(document.getElementsByClassName('testing'));

Or take in account that it's live:

while (elements.length) elements[0].classList.remove('element-focus');
like image 73
plalx Avatar answered Oct 19 '22 19:10

plalx


Using document.getElementsByClassName returns a live HTMLCollection:

An HTMLCollection in the HTML DOM is live; it is automatically updated when the underlying document is changed.

Thus (as described in plalx's answer) if you remove an element's class, the element is removed from an HTMLCollection based on that class.

Instead you could use document.querySelectorAll which returns a static NodeList collection:

The Document method querySelectorAll() returns a static (not live) NodeList representing a list of the document's elements that match the specified group of selectors.

So your code would change from

var elements = document.getElementsByClassName("testing");

with the class name "testing" as an argument, to

var elements = document.querySelectorAll(".testing");

with the class selector ".testing" as an argument.

And then you could iterate over elements which would be a static NodeList.

like image 23
jkdev Avatar answered Oct 19 '22 18:10

jkdev