- From: Maciej Stachowiak <mjs@apple.com>
- Date: Fri, 2 Feb 2007 08:19:31 -0800
I looked into DOMTokenString with keen interest, because I believe an API for manipulating individual classes of an HTML element is increasingly important as we see more dynamic sites that use CSS styling. However, I think the design for this is not suitable as-is. Summary of DOMTokenString ---------------------------------- Web Apps 1.0 introduces the DOMTokenString interface, and uses it for the className attribute on HTMLElement instead of DOMString; and on a few DataGrid methods: void getCaptionClasses(in unsigned long column, in DOMTokenString classes); void getRowClasses(in RowSpecification row, in DOMTokenString classes); void getCellClasses(in RowSpecification row, in unsigned long column, in DOMTokenString classes); DOMTokenString has the following interface: interface DOMTokenString : DOMString { boolean has(in DOMString token); void add(in DOMString token); void remove(in DOMString token); }; In addition the spec says: "In the ECMAScript DOM binding, objects implementing the DOMTokenString interface must stringify to the object's underlying string representation. DOMTokenString inherits from DOMString, so in bindings where strings have attributes or methods, those attributes and methods will also operate on DOMTokenString objects." Issues ------- This approach is clever and solves the long-standing need to manipulate individual classes via the DOM API. There are a few technical problems, however: 1) In ECMAScript, the DOMString interface represents a String primitive value, not a String object. There is no way to "subclass" primitive types. (When you invoke a method on a string it actually makes a wrapper for it on the fly.) There's no way to make an object type appear just like a string, since the typeof operator for instance will know the difference. Even to make the String methods and properties work, you'd likely have to reimplement them, which is annoying for implementors. Java strings are also not subclassable. 2) ECMAScript strings are always immutable. This interface introduces operations that mutate the string. This violates the contract of JS strings - if you store one of these in an object, its value may change before you can retrieve it. Java strings are also immutable. 3) It seems to be implied that the DOMTokenString should be a reference to, rather than a copy of the underlying attribute value. Presumably doing myElement.className.add("foo") is intended to actually modify the elements "class" attribute, and not just a temporary DOMTokenString object. However, many implementations copy DOM string values before returning them to JS because the underlying type is not the same. 4) Semantics of assigning to the className field are odd. The spec says to the string value even if you provide a DOMTokenString, but this is somewhat inconsistent, as presumably var someClass = myElt.className does not copy. While the DOM is already full of magic properties that do odd things on assignment, I'm unusure of the benefit of making className one of them. I do not think the cleverness of this idea so great as to be worth these problems (especially #2). Possible alternate designs -------------------------- Alternative #1: leave className an ordinary string, but add the following methods to the HTMLElement interface: void addClass(DOMString newClass); void removeClass(DOMString removeClass); bool hasClass(DOMString possibleClass); This is no more API surface area, and the semantics are much more clear; methods that modify the HTMLElement's class attribute are on the element itself. An additional method to get an array of the tokenized class strings could also be useful, but that starts getting into the territory of the other option. Alternative #2: leave the className an ordinary string, but add a new readonly DOMClassList classList property with something like the following interface: interface DOMClassList { void add(DOMString newClass); void remove(DOMString removeClass); bool has(DOMString possibleClass); } If you add DOMString index(unsigned i) and unsigned length, you would also have the ability to enumerate the classes easily, which the API as currently specced lacks. Because the classList property would be readonly, there would be no question of whether two elements ever share a DOMClassList. I like #1 better, but either seems fine. In both of these alternatives, the DataGrid methods would be changed to return a string instead of taking and modifing an existing DOMTokenString. It is in any case highly unusual in DOM APIs for getters to mutate a provided "out" object rather than to return a value. Also the spec does not appear to provide a way to make a brand new empty DOMTokenString, so these methods would otherwise only be useful for altering the class of existing elements based on the classes of some other elements, which seems unuseful. Regards, Maciej
Received on Friday, 2 February 2007 08:19:31 UTC