2006/07/22

E4X and the DOM: another take on conversion, more reasons to "grr".

So, here's me writing my cool graphing thing (if this were in sillyvalley, it could easily be half the IP assets of a startup and the key to USD1M funding :-p), when, looking for real-world e4x examples I find an interesting blog post by ecmanaut linking to this email by Mor Roses, chiefly concerning the lack of E4X<->DOM, e.g. (to take Johan's example) bemoaning the impossibility of node.appendChild( <img src="url" /> ).

Me, I'm 100% in agreement that this kind of thing is a definite oversight, but when one considers that the DOM API is governed by the W3C while E4X and ECMAScript are both ECMA standards, one could charitably understand why ECMA didn't want to tread on a fellow standards body's toes. Or, more cynically, promote the use of competing specs.

Anyway. This becomes one more hurdle at the presentation level, which I can live with. After having written DOM-driven SAX event drivers and DOM-constructing SAX event handlers, not even touching XMLPull etc., why not add another to the mix? Bring the noise.

Which brings me to my next point, which is that I can rarely resist using JS code that I haven't fiddled with. The functions in the above links are functionally perfect, but I don't get why the XMLNS is hard-coded and not passed as an argument, since FF can grok SVG and MathML now. Conversely, the mime type of "text/xml" is a static constant, when, for the purposes of the parser, it will never change. Additionally, having a Java background means I instinctively namespace my code, meaning I don't have to assign anything to the function properties.

My purely aesthetic revision:

lib.util.e4x2dom = function(xmlObj, xmlObjNs, doc) {

if(!doc) doc = document;
if(!xmlObjNs) xmlObjNs = NS_HTML;

var xmlRootObj = <root xmlns={xmlObjNs} />;

if(!lib.util.e4x2dom.parser) lib.util.e4x2dom.parser = new DOMParser(); //one time initialisation

xmlRootObj.firstChild=xmlObj;

var domTree=lib.util.e4x2dom.parser.parseFromString(xmlRootObj.toXMLString(), "text/xml");

var importMe=domTree.documentElement.firstChild;

while(importMe && importMe.nodeType!=1) {
importMe=importMe.nextSibling;
}

return (importMe) ? doc.importNode(importMe,true):null;
}

Here's hoping that blogspot doesn't mangle my escaped & formatted characters.

HAVING SAID THAT,

A second consequence of the unwanted duality is (joy!) complete lack of access to DOM-specific functions. In HTML this isn't such a big deal, but in SVG when you construct text elements, the getComputedTextLength() method is invaluable and is often used during the construction of the element, i.e. in my case when it's still in E4X and not in DOM.

So this is what I'm left with:


var text = "foo bar";
var textDOM = document.createElement("text");
textDOM.appendChild(document.createTextNode(text));
var textE4X = <text y="0" x={textDOM.getComputedTextLength()}>{text}</text>;



Which is a nasty hack, to put it nicely. No code that touches the real world will ever be perfect.

Addendum: the above snippet doesn't work. The text has to be rendered before FF can give me a length. Foo.