Cross browser XML
by Jan Verhoeven, July 31, 2008
When Internet Explorer 5 came out, I did a lot of experiments with XML in the browser. Unfortunately my code dit not work in other browsers.
We are now 8 years later and all modern browsers support XML and XPath. Although some of them still do not support XSLT.
Two years ago I started working on a browser based XML editor because it seemed we needed one at work. Before I could finish the editor the Altova Authentic ActiveX control was used. So, I parked my personal project. As usual, when someone comes up with an IE only solution (as is the Authentic solution), it is just a matter of time before clients are not satisfied with being tied to IE for their XML editing. Therefore I proceeded with my work on the browser based XML editor. And now I am in the finishing stage.
Writing an XML editor is one thing. Writing an XML editor that works in IE, FF, Opera and Safari is a different piece of cake. But I got it working. I will not describe the XML editor itself here, but only the cross-browser bits.
I made a list of XML things that I needed.
- The ability to load an XML document from an xml string, as I wanted to editor to be completely ignorent of the outside world.
- The ability to serialize the XML document to an xml string that I could send back to the server using an Ajax-call.
- Using selectSingleNode and selectNodes with XPath expressions.
I searched the web for the required routines and embedded those into my own javascript classes.
Getting and setting xml
For all browsers except IE I added the following prototype functions.
Document.prototype.loadXML = function (s) {
// parse the string to a new doc
var doc2 = (new DOMParser()).parseFromString(s, "text/xml");
// remove all initial children
while (this.hasChildNodes())
this.removeChild(this.lastChild);
// insert and import nodes
for (var i = 0; i < doc2.childNodes.length; i++) {
this.appendChild(this.importNode(doc2.childNodes[i], true));
}
};
Document.prototype.__defineGetter__("xml", function () {
return (new XMLSerializer()).serializeToString(this);
});
Selecting nodes
Again for all non IE browsers I added 2 functions to Node.prototype.
Node.prototype.selectNodes = function (sXPath)
{
var xpe = new XPathEvaluator();
var nsResolver = xpe.createNSResolver(this.ownerDocument == null ?
this.documentElement : this.ownerDocument.documentElement);
var oResult = xpe.evaluate(sXPath, this, nsResolver,
XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
var aNodes = new Array;
if (oResult != null) {
var oElement = oResult.iterateNext();
while(oElement) {
aNodes.push(oElement);
oElement = oResult.iterateNext();
}
}
return aNodes;
};
Node.prototype.selectSingleNode = function (sXPath)
{
var xpe = new XPathEvaluator();
var nsResolver = xpe.createNSResolver(this.ownerDocument == null ?
this.documentElement : this.ownerDocument.documentElement);
var oResult = xpe.evaluate(sXPath, this, nsResolver,
XPathResult.FIRST_ORDERED_NODE_TYPE, null);
if (oResult != null)
{
return oResult.singleNodeValue;
}
else
{
return null;
}
};
Getting and setting node text
Node.prototype.__defineGetter__("text", function () {
return(this.textContent);
}); // text
Node.prototype.__defineSetter__("text", function (txt) {
this.textContent = txt;
});
Almost there
Now that I could access the XML documents in a uniform way I though I was ready with the cross browser stuff.
Getting and setting node attribute values is something that is done all the time in a XML editor. Because my XML editor is schema based and I wanted to add meta data to the schema using a namespace, I discovered a difference between Opera and the other browsers.
In my schema I have attributes like meta:label-nl. In all browsers except opera I can retrieve the value of that attribute with a simple node.getAttribute("meta:label-nl"). No so in opera.
In Opera you have to use the namespaceURI in combination with the local name of the attribute.
if (window.opera)
{
var value = node.getAttribute("http://jansfreeware.com/meta", "label-nl");
}
And of course I have the corresponding xmlns:meta="http://jansfreeware.com/meta" declaration in my schema document element. You must use the full URI of the namespace and not the meta alias.