Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I replace one class with another on all elements, using just the DOM?

Tags:

javascript

dom

I just want to change a classname to another one.

I've tried using

document.getElementsByClassName("current").setAttribute("class", "none");

but it doesn't work. I'm new at javascript.

like image 532
Lohn Claidon Avatar asked May 12 '13 19:05

Lohn Claidon


People also ask

How do you replace a DOM element?

To replace a DOM element in the DOM tree, you follow these steps: First, select the DOM element that you want to replace. Then, create a new element. Finally, select the parent element of the target element and replace the target element by the new element using the replaceChild() method.


2 Answers

Explanation

document.getElementsByClassName returns an HTMLCollection, not just an array of elements. That means that the collection is live, so in this specific situation, it retains the requirement that it will always hold all elements with the class "current".

Coincidentally, you are removing the very class that the collection depends on, therefore updating the collection. It would be totally different if you were setting the value property (for example) in the loop - the collection wouldn't be affected, because the class "current" wasn't removed. It would also be totally different if you were adding a class, such as el.className += " none";, but that isn't the case.

A great description from the MDN docs:

HTMLCollections in the HTML DOM are live; they are automatically updated when the underlying document is changed.

Approach 1

An easy way to overcome all this pandemonium is by looping backwards.

var els = document.getElementsByClassName('current'),
    i = els.length;

while (i--) {
    els[i].className = 'none';
}

DEMO: http://jsfiddle.net/fAJgT/

(the code in the demo has a setTimeout, simply so you can see the original border color at first, then after 1.5 seconds, see it change)

This works because it modifies the last item in the collection - when it is modified (and automatically removed), move onto the item before it. So it doesn't suffer any consequences of the automatic removal.

An alternate setup, doing the same thing, is:

for (i = els.length; i >= 0; i--) {

Approach 2

Another answer made me realize you could just continually operate on the first item found. When you remove the specific class, the element is removed from the collection, so you're guaranteed that the first item is always a fresh item in the collection. Therefore, checking the length property should be a safe condition to check. Here's an example:

var els = document.getElementsByClassName('current');
while (els.length) {
    els[0].className = 'none';
}

DEMO: http://jsfiddle.net/EJLXe/

This is basically saying "while there's still items in the collection, modify the first one (which will be removed after modified)". I really wouldn't recommend ever using that method though, because it only works specifically because you end up modifying the collection. This would infinitely loop if you were not removing the specific class, or with a normal array or a non-live collection (without spliceing).

Approach 3

Another option is to slice (shallow copy) the collection into an array and loop through that normally. But I don't see any reason/improvement to do that. Here's an example anyways:

var els = document.getElementsByClassName('current'),
    sliced = Array.prototype.slice.call(els), i;

for (i = 0; i < sliced.length; i++) {
    sliced[i].className = 'none';
}

DEMO: http://jsfiddle.net/LHe95/2/

Approach 4

Finally, you could use document.querySelector - it returns a non-live NodeList (therefore you can loop like normal), and even has better support in browsers than document.getElementsByClassName does. Here's an example:

var els = document.querySelectorAll('.current'),
    i;

for (i = 0; i < els.length; i++) {
    els[i].className = 'none';
}

DEMO: http://jsfiddle.net/xq8Xr/


References:

  • document.getElementsByClassName: https://developer.mozilla.org/en-US/docs/DOM/document.getElementsByClassName
  • HTMLCollection: https://developer.mozilla.org/en-US/docs/DOM/HTMLCollection
  • slice: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/slice
  • querySelectorAll: https://developer.mozilla.org/en-US/docs/DOM/Document.querySelectorAll
like image 176
Ian Avatar answered Sep 28 '22 01:09

Ian


document.getElementsByClassName("current") returns a HTMLCollection, not an HTMLElement. You can access elements in the list the same way you would access an array.

The following will change the class of the first element in the HTMLCollection.

document.getElementsByClassName("current")[0].setAttribute("class", "none");

However, you do not have to use the setAttribute method, you can just set the className property of the element.

 element.className = 'none';

Please note that HTMLCollection are live, wich means that as soon as the element will not have the current class name anymore, the element will be removed from the array, so to avoid any issues you could iterate over it using the following approach:

var list = document.getElementsByClassName("current");

while (list.length) {
    list[0].className = 'none';
}
like image 45
plalx Avatar answered Sep 28 '22 00:09

plalx