UsefulJS.ClassList

Manipulating element classes is the very bread-and-butter of DHTML. Almost every JavaScript developer will have written library functions to do this and class manipulation functions are core parts of every JavaScript framework. HTML5 has brought a standardized interface for this, DOMTokenList, and the classList property on element objects that implements it.

UsefulJS.ClassList provides a portable, complete implementation of the DOMTokenList interface for manipulating class lists. On browsers that lack a native implementation, but which have the necessary Object.defineProperty support (in particular Internet Explorer versions 8 and 9), the module fix will add a classList property to elements that you can use to manipulate the underlying className. On legacy browsers that lack both a native implementation and Object.defineProperty, the module provides a static method, get, that will return a DOMTokenList for the element.

On browsers that fully support Object.defineProperty, UsefulJS.ClassList also implements the DOMSettableTokenInterface where the underlying className can be accessed via a value property.

Syntax
new UsefulJS.ClassList(obj[, propertyName[, separator]])
Parameters

Throws: TypeError if obj is null or undefined.

UsefulJS.ClassList instances are not limited to managing element class names. They can manage any property on any object whose value is a delimiter-separated token list.

Static properties

UsefulJS.featureSupport.classList

Adds a classList entry to UsefulJS.featureSupport that allows for testing classList support. Exposes three properties:

settable

true when instances of UsefulJS.ClassList have a value property that can be used to access the underlying className. It will be undefined otherwise (because it won't exist!).

Static methods

get

Returns a classList object for an element.

Syntax
UsefulJS.ClassList.get(elem)
Parameters

Returns: DOMTokenList (or null if elem is null or an ID that can't be resolved).

Description

Returns the classList of the element, if present. Otherwise an instance of UsefulJS.ClassList is returned.

Usage
var elem = document.documentElement,
    classList = UsefulJS.ClassList.get(elem);

DOMTokenList interface

Instance properties

length

Read-only property that reflects the number of classes in the underlying classname.

This property is read-only in browsers that support Object.defineProperty. Otherwise it can be written to. Assigning a value to it doesn't change the underlying className.

[0]...[n]

classList objects are array-like and may be addressed through [] indexing. Native classList objects cannot have values assigned to their numeric properties. Instances of UsefulJS.ClassList can; doing so doesn't change anything, but the values obtained will no longer be truthful. So don't!

Instance methods

add

Adds one or more class names to the element's className

Syntax
classList.add(cl1[, cl2...])

contains

Returns true if the element's className contains the specified token

Syntax
classList.contains(cl)

item

Returns the value of the token in the element's className at the specified position. Returns null if the position is out of range. I can't quite see the point of this when you have [] indexing.

Syntax
classList.item(pos)

remove

Removes one or more class names from the element's className

Syntax
classList.remove(cl1[, cl2...])

toggle

Adds a class name and returns true if not present; removes a class name and returns false if present.

Syntax
classList.toggle(cl[, force])
Description

The optional force parameter changes the semantics. If its value is true, the function will add a class but not remove it. It will then return true if the token is present. If its value is false, the function will remove a class but not add it. This allows for test-and-set logic:

if (classList.toggle("myClass", true)) {
    // Do something
    ...
}

The behaviour is different from add, which also leaves the value unmodified if the class is set, because add doesn't return a value.

toString

Stringifies to the underlying className

Syntax
classList.toString()

DOMSettableTokenList interface

Instance properties

value

Read-write property that allows direct access to the underlying className. Since you have the className, I can't quite see the point of this, but I implemented the interface for completeness.

Implementation notes

UsefulJS.ClassList needs to do a fair amount of housekeeping since the underlying className may be modified at any time during the lifetime of the object. It checks the className value against the expected value on each method call. If they differ, it re-initializes itself. This will really hurt performance so don't modify the className directly if you're also modifying it through a classList object.

The need to present an array-like interface to the world means that numeric properties need to be recalculated on each modification. This means that it is never going to be as performant as a native implementation. That said, unless you're modifying hundreds of classes on hundreds of elements. the overhead shouldn't be noticeable.

The specification requires that certain error types be raised if a class name passed to a method is null or contains spaces. Since these errors cannot be constructed on legacy browsers (where you're likely to be using this), I decided simply to ignore bad class names.

Portability notes

With native implementations, don't depend on being able to pass multiple arguments to add and remove. Don't depend either on being able to use the force parameter with toggle. No native implementation as far as I can tell implements the DOMSettableTokenList interface which is no big loss. If you need to support legacy browsers, you can reliably use UsefulJS.ClassList.get.

Fixes

The fixes for the ClassList module are defined in the _classList namespace of the fix options.

classList

Adds a classList property to elements when possible. This fix is applied by default. The descriptor for the added property has enumerable set to false and configurable set to true. This is the magic combination that works on Internet Explorer 8.