Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

is there any harm in adding a class to an element that already has that class using .classLIst

Tags:

javascript

css

This question's answer (addClass to an element with that class already?) indicates that when using jQuery there is no problem that arises if you .addClass('foo') on an element that already has a class of foo.

I am curious of the same is true for the element.classList method of .add.

In particular I have a function called update() that is called whenever a range slider is updated. This can occur many times a second. Depending on the value passed to update() I add and remove certain classes to an element.

If the value falls in a certain range consecutively I end up adding the same class over and over again.

My question is can I allow elem.classList.add('foo') to run, let's say, 50 times in one second without experiencing any negative consequences to user experience, memory, processor use, etc. Is this an acceptable practice?

Thanks.

like image 456
WillD Avatar asked Jul 06 '18 16:07

WillD


1 Answers

DON'T do this:

if (!el.classList.contains("foo")) {
    el.classList.add("foo");
}

DO this:

el.classList.add("foo");

Reason:

  • The .classList property is a DOMTokenList object.
  • It represents a set of whitespace-separated tokens.
  • The tokens (i.e. the class names) are case-sensitive.
  • The term "set" in programming refers to a container that holds a unique collection of items, in unspecified order.
  • In other words, a set cannot contain duplicates.
  • The DOM specification which defines the DOMTokenList defines it as a container with case-sensitive, whitespace-trimmed, unique items.
  • All methods that read or modify the DOMTokenList (such as DOMTokenList.add()) automatically trim any surrounding whitespace and remove duplicates from the set.
  • Meaning that if you try to add() the same className twice, it won't add the duplicate.

Here's an example:

<span class="    d   d e f"></span>

// Output: DOMTokenList(3) ["d", "e", "f", value: "    d   d e f"]
// As you can see, it only contains the unique values.
console.log(document.querySelector("span").classList);

// Try to add a duplicate value.
document.querySelector("span").classList.add("e");

// Output: DOMTokenList(3) ["d", "e", "f", value: "d e f"]  
// As you can see again, it only contains the unique values.
console.log(document.querySelector("span").classList);

// In fact, the act of duplicates being detected while trying
// to add more classes, results in the "class" attribute itself
// being cleaned up and trimmed:
// Output: "d e f"
console.log(document.querySelector("span").getAttribute("class"));

Alright, so why, exactly, shouldn't you use .contains() to "avoid calling .add() when unnecessary"? Because:

  • The .contains() function and the .add() functions use the exact same native code for searching through the unique set to see if it contains the class.
  • Therefore there's no speed difference between their techniques for detecting whether a className exists.
  • In other words, calling .add() will search through the set, and if it already contains the className, the function simply aborts. And yes, it sometimes also cleans up the "class" HTML attribute if necessary, but only if necessary.
  • On the other hand, if you call .contains() before .add() and you end up adding the className, you just ran the search for the className twice, via two different functions.
  • But more importantly: You make your code clunky and unreadable by having that huge if (!el.classList.contains("foo")) { ... } boilerplate around every attempt to add classes.
  • The DOM spec was made to be modern and performant for the use-cases programmers need it for. That means, that you should be using .add() to add a class (even if it already exists), and .remove() to remove a class (even if it's already missing).

Don't waste your keyboard's life and programmer eyes on clunky boilerplate. Only use .contains() if you actually care about finding out if an element has a certain class. 😊

Also keep in mind that the DOMTokenList has many other helpful functions:

  • See https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList.
  • .replace(oldToken, newToken): Replaces an existing token with a new token. If the old token doesn't exist, .replace() returns false immediately, without adding the new token to the token list.
  • .toggle(token [, force]): Removes a given token from the list and returns false, or if token doesn't exist it's instead added and the function returns true. Alternatively, if you include the force parameter: If included, turns the toggle into a one way-only operation. If set to false, then token will only be removed, but not added. If set to true, then token will only be added, but not removed.
  • Those two extra functions will let you implement most of things you'd ever wanna do instead of "if not .contains() then .add()" or similar code. You should ONLY be using ".contains()" if YOU personally need to know if a token exists.
like image 64
Mitch McMabers Avatar answered Nov 03 '22 00:11

Mitch McMabers