Mozilla

ClassList in Firefox 3.6

This article was writt by Anthony Ricaud, French OpenWeb enthusiast.

Why you need classList

A dynamic web application usually needs visual feedback from its inner mechanism or needs to display different visual elements based on users’ actions.

To change the user interface easily, you can add/remove/edit elements through the DOM API (document.createElement, div.removeChild, elt.style.color, …) but it’s easier to just update the elements’ class attribute to change how they are displayed and styled by CSS.

Let’s take an example. Suppose you want to display a form with two modes: a basic mode, and an expert mode with extra options.

This can be done with CSS rules: each mode has its own class and set of CSS code.

#anexpertinput.basic {
  display: none;
}
#anexpertinput.expert {
  display: inline;
}

To dynamically change the classes of elements, you can use element.className. However, you may want to add, remove, or toggle just one class. There used to be two ways to do this, by using a library or by writing complex code with regular expressions. There is now another way with the new HTML5 API called classList, which is implemented in Firefox 3.6.

Let’s see how it can simplify your code and improve performance at the same time.

The classList API

Here is an example to show you what the classList API looks like:

// By default, start without a class in the div: <div class=""/>
 
// Set "foo" as the class by adding it to the classList
div.classList.add('foo'); // now <div class="foo"/>
 
// Check that the classList contains the class "foo"
div.classList.contains('foo'); // returns true
 
// Remove the class "foo" from the list
div.classList.remove('foo'); // now <div class=""/>
 
// Check if classList contains the class "foo"
div.classList.contains('foo'); // returns false: "foo" is gone
 
// Check if class contains the class "foo",
// If it does, "foo" is removed, if it doesn't, it's added
div.classList.toggle('foo'); // class set to <div class="foo"/>
div.classList.toggle('foo'); // class set to <div class=""/>

Demo

Let’s go back to our initial example of a form with both a basic and an expert mode – check out the live demo to see it in action.

As you can see in the code below, you can switch between the two modes with one line of JavaScript.

<button onclick="document.getElementById('box').classList.toggle('expert');">
  Toggle expert mode
</button>
<div id="box">
  <label for="nick">Name: <input type="text" id="nick" /></label>
  <label for="status">Status: <input type="text" id="status" /></label>
  <p id="help"> Blablablablabla...</p>
  <label for="postpone">Postpone: <input type="checkbox" id="postpone" /></label>
  <label for="lang">Lang: <input type="text" id="lang" /></label>
</div>
#box.expert > #help,
#box.expert > label[for="postpone"],
#box.expert > label[for="lang"] {
   display: none;
}

See the Mozilla documentation and the HTML5 specification for more details on classList.

Performance

Using the classList API is not only easier, it’s also more powerful. Take a look at what we observed using Firefox 3.6.

benchmark classList

Interoperability

Since other browser vendors have not yet implemented the HTML5 classList API, you still need fallback code. You can use this sample code as fallback.

To know more about the current implementation of classList in well-known JavaScript libraries, see:

16 comments

Comments are now closed.

  1. thinsoldier wrote on January 29th, 2010 at 13:00:

    re: http://demos.hacks.mozilla.org/openweb/classList/

    The “normal test” in Safari is faster than the “native” test in Firefox :(

  2. shawnee wrote on January 29th, 2010 at 13:11:

    Awesome! We’ve needed a decent explanation for this web browser behavior for a long time. . . I love how most JavaScript is just glorified, commented CSS behaviors.

  3. Boris wrote on January 29th, 2010 at 13:12:

    The linked benchmark seems pretty useless; in particular it mostly measures how long it takes to parse very long class attribute values in the engine. Since the use case of setting or unsetting 1000 differently classes is not a common one, why are we testing it?

  4. Edwin Martin wrote on January 29th, 2010 at 13:27:

    Testing extreme situations can be very useful. If your feature works and is fast enough in extreme situations, you can be pretty sure it will perform in normal situations.

  5. shawnee wrote on January 29th, 2010 at 14:15:

    Further proof for my comment: http://arstechnica.com/apple/news/2008/04/javascript-slows-down-the-internet-webkit-to-the-rescue.ars

  6. Boris wrote on January 29th, 2010 at 19:28:

    Edwin, you would be right if the extreme case has the same bottlenecks as normal use. If it doesn’t, then it really doesn’t tell you much.

    Or put another way, I can speed up this “extreme case” probably by a factor of 2-3 without too much trouble without speedin up the normal case; in fact it would probably slow down the normal case.

  7. Azat Razetdinov wrote on January 31st, 2010 at 03:35:

    There’s strong need for the second parameter in the toggle method:
    classList.toggle(‘selected’, someObject.isSelected()).

  8. Anthony Ricaud wrote on February 1st, 2010 at 10:42:

    The point of this article is not the benchmark, it’s the new and easier API.

  9. Paul Rouget wrote on February 1st, 2010 at 11:15:

    About the benchmark, I agree that it’s not that representative of the usual case. But you can’t just use a normal use case to explain and show such improvements (the performance is here, but not noticeable enough).

    Exaggerating the test case is a good way to demonstrate the enhancement.

    But as Anthony said, the point of this article is the API which is way easier to use.

  10. Boris wrote on February 1st, 2010 at 11:41:

    Anthony, Paul, you could trivially write a benchmark that would be more representative of actual usage by simply adding to multiple elements, or timing add + remove on the same element or whatever way people will _really_ use this. If you can’t figure out a way that people would use it such that an improvement is visible, then the perf difference simply doesn’t matter.

  11. WalterK wrote on February 3rd, 2010 at 13:31:

    In the demo, when the additional elements are “hidden”, copying and pasting text selection that includes “hidden” area still contains “hidden” elements. Is this an unfortunate side effect or there is a way to actually hide the elements from copy/pasting? I’ve seen similar annoying side effects on commercial websites that overly rely on JS/CSS for no good reason.

  12. nemo wrote on February 4th, 2010 at 12:35:

    Hey WalterK. That’s obviously because the “hide” is using display: none;

    If you want it to work with copy/paste the solution is to add/remove that block from the DOM. That can be done fairly easily/quickly, esp if the block is nicely contained.

    Of course, if you’re going to do that, it is best to make visible the default, and the hide only on a javascript action (like an onclick). That way the form will degrade well for the non-javascript case.

    However, I use display: none; all the time myself. I don’t really see this copy/paste issue as being that crucial for things I’m showing/hiding.

    In this case for example, I really don’t see why it should matter to you at all.
    And given the large number of hidden things on the web these days, scripts and other junk, copying large portions of page is guaranteed to pick all kinds of things up.

  13. Pingback from Blue Sky On Mars » Blog Archive » Bespin and jQuery on May 26th, 2010 at 06:56:

    [...] was happy to hear from Thomas Bassetto that addClass has indeed been rendered redundant by HTML5’s ClassList API which looks great. element.classList is available today in Firefox 3.6 and, according to the [...]

  14. austin wrote on June 22nd, 2010 at 11:55:

    its often hard to notice where you are getting slowed down with small scale tests, you have one or two and the factor is so small your instruments just cant register it, so you exaggerate it, you take it to the extreme and see if you STILL see good performance. its a test of overall performance but also of scalability (someone may actually end up with very large class lists i could see me having large class lists over the course of a long development cycle, also if its built into some sort of library or api people will add a bit to it, til you have class lists in the 10s or 100s. while probably not too common, yet, i expect it to be a real outcome of the new class list api.)
    but the point was to show that there is a difference in performance one way or the other between the native and non-native api (which can sometimes make the difference in large libraries that need to be calling the function a few hundred times a second)

  15. Pingback from Firefox 3.6에서 지원되는 ClassList ✩ Mozilla 웹 기술 블로그 on July 20th, 2010 at 06:17:

    [...] 원저자: Anthony Ricaud – 원문으로 가기 [...]

  16. cc young wrote on July 18th, 2011 at 06:46:

    notes:

    if classList.add(‘c1′) and c1 is already in the classList, benign; that is, if classList.remove(‘c1′) then classList.contains(‘c1′) is false

    if classList.add(‘c1 c2′) => error; whereas className(‘c1 c2′) adds both classes

Comments are closed for this article.