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: 
// Set "foo" as the class by adding it to the classList div.classList.add('foo'); // now
// 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
// 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.classList.toggle('foo'); // class set to

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.


Blablablablabla...

#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:

About Paul Rouget

Paul is a Firefox developer.

More articles by Paul Rouget…


16 comments

  1. thinsoldier

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

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

    January 29th, 2010 at 13:00

  2. shawnee

    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.

    January 29th, 2010 at 13:11

  3. Boris

    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?

    January 29th, 2010 at 13:12

  4. Edwin Martin

    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.

    January 29th, 2010 at 13:27

  5. shawnee

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

    January 29th, 2010 at 14:15

  6. Boris

    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.

    January 29th, 2010 at 19:28

  7. Azat Razetdinov

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

    January 31st, 2010 at 03:35

  8. Anthony Ricaud

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

    February 1st, 2010 at 10:42

  9. Paul Rouget

    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.

    February 1st, 2010 at 11:15

  10. Boris

    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.

    February 1st, 2010 at 11:41

  11. WalterK

    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.

    February 3rd, 2010 at 13:31

  12. nemo

    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.

    February 4th, 2010 at 12:35

  13. […] 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 […]

    May 26th, 2010 at 06:56

  14. austin

    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)

    June 22nd, 2010 at 11:55

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

    July 20th, 2010 at 06:17

  16. cc young

    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

    July 18th, 2011 at 06:46

Comments are closed for this article.