1

I have an element like this :

<span class="util bg-color-yellow tooltip bg-color-red"></span>

And I wanted to remove all class starting with "bg-color-", so I would simply loop thru all the class, then remove the matched ones :

var element = document.getElement......;

var elementClasses = element.classList;


for( var i = 0, l = elementClasses.length; i < l; i++ )
{
    var className = elementClasses[i];

    // console.log(i);
    // console.log(name);

    if( className.startsWith('bg-color-') )
    {
        element.classList.remove(className);
    }

    console.log('elementClasses contain :');
    console.log(elementClasses);
}

When it matches the 'bg-color-' class, it successfully remove the class name, but it also edit the variable elementClasses as if it's a reference to element.classList, even if it's a variable ( copy ) of it.

1st loop :

elementClasses = [ util bg-color-yellow tooltip bg-color-red ]

2nd loop :

elementClasses = [ util bg-color-yellow tooltip bg-color-red ]

3nd loop :

elementClasses = [ util tooltip bg-color-red ]

There goes the problem! elementClasses is modified on the same time as the elemnt.classList is modified!

  • How is that possible ?
  • Is effectively elementClasses a sort of reference of element.classList ?
  • Did elementClasses recreated it self each time element.classList is modified ?

.

Thank you very much! :)

Spidery
  • 43
  • 5

3 Answers3

2

It's good that you've identified what's causing the problem. Now for the solution:

// ES6
[...elementClasses].forEach((className, i) => {
// OR ES5
Array.prototype.slice.call(elementClasses).forEach(function (className, i) {

    // console.log(i);
    // console.log(className);

    if( className.startsWith('bg-color-') )
    {
        element.classList.remove(className);
    }

    console.log('elementClasses contain :');
    console.log(elementClasses);
});

Using either of these methods, the classList is shallow-copied to ensure that you are not skipping entries when an array is modified.

To answer your other question, elementClasses in your original code is indeed a reference to element.classList, which is why both of them were modified when one of them was.

In my ES6 suggestion, [...elementClasses] uses the spread operator to make a shallow copy into a native Array, while the ES5 method calls slice() on the original element.classList to make a shallow copy of it.

Now for a demo:

var element = document.querySelector('span');
var elementClasses = element.classList;

// ES6
// [...elementClasses].forEach((className, i) => {
// OR ES5
Array.prototype.slice.call(elementClasses).forEach(function (className, i) {

    console.log(i);
    console.log(className);

    if( className.startsWith('bg-color-') )
    {
        element.classList.remove(className);
    }

    console.log('elementClasses contain :');
    console.log(JSON.stringify(elementClasses));
});
<span class="util bg-color-yellow tooltip bg-color-red"></span>
Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
0

In your implementation elementClass is not actually a copy of element.classList. It is a reference to the same object. To copy the object consider using .slice().

See this question: Copying array by value in JavaScript

Community
  • 1
  • 1
fqhv
  • 1,191
  • 1
  • 13
  • 25
0

Please see this post this explains the answer of how javascript copies variables when you use '=' sign

Copy a variable's value into another

Community
  • 1
  • 1
alyn000r
  • 546
  • 2
  • 8
  • 19
  • wow, thanks I never heard of javascript reference before. It sure would be very helpful for the future! – Spidery Apr 02 '17 at 01:01