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.
new UsefulJS.ClassList(obj[, propertyName[, separator]])
obj
ObjectpropertyName
Stringseparator
StringThrows: TypeError if obj
is null or undefined.
Adds a classList entry to UsefulJS.featureSupport that allows for testing classList
support.
Exposes three properties:
Whether the classList
property is available on elements.
Whether the classList
property is natively implemented. When classList support
has been added via the fix, supported
will be true, but
nativeSupport
will be false.
Whether the classList implementation supports the DOMSettableTokenList
interface.
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!).
Returns a classList object for an element.
UsefulJS.ClassList.get(elem)
elem
Element or StringReturns: DOMTokenList (or null if elem
is null or an ID that can't
be resolved).
Returns the classList of the element, if present. Otherwise an instance of UsefulJS.ClassList is returned.
var elem = document.documentElement, classList = UsefulJS.ClassList.get(elem);
Read-only property that reflects the number of classes in the underlying classname.
Object.defineProperty
. Otherwise it can be written to. Assigning a value to
it doesn't change the underlying className.
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!
Adds one or more class names to the element's className
classList.add(cl1[, cl2...])
Returns true if the element's className contains the specified token
classList.contains(cl)
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.
classList.item(pos)
Removes one or more class names from the element's className
classList.remove(cl1[, cl2...])
Adds a class name and returns true if not present; removes a class name and returns false if present.
classList.toggle(cl[, force])
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.
Stringifies to the underlying className
classList.toString()
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.
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.
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
.
The fixes for the ClassList module are defined in the _classList namespace of the fix options.
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.