Chapter 18

XML in JavaScript

WHAT’S IN THIS CHAPTER?

  • Examining XML DOM support in browsers
  • Understanding XPath in JavaScript
  • Using XSLT processors

At one point in time, XML was the standard for structured data storage and transmission over the Internet. The evolution of XML closely mirrored the evolution of web technologies, as the DOM was developed for use not just in web browsers but also in desktop and server applications for dealing with XML data structures. Many developers started writing their own XML parsers in JavaScript to deal with the lack of built-in solutions. Since that time, all browsers have introduced native support for XML, the XML DOM, and many related technologies.

XML DOM SUPPORT IN BROWSERS

Since browser vendors began implementing XML solutions before formal standards were created, each offers not only different levels of support but also different implementations. DOM Level 2 was the first specification to introduce the concept of dynamic XML DOM creation. This capability was expanded on in DOM Level 3 to include parsing and serialization. By the time DOM Level 3 was finalized, however, most browsers had implemented their own solutions.

DOM Level 2 Core

As mentioned in Chapter 12, DOM Level 2 introduced the createDocument() method of document.implementation. Internet Explorer 9+, Firefox, Opera, Chrome, and Safari support this method. You may recall that it’s possible to create a blank XML document using the following syntax:

var xmldom = document.implementation.createDocument(namespaceUri, root, doctype); 

When dealing with XML in JavaScript, the root argument is typically the only one that is used, because this defines the tag name of the XML DOM’s document element. The namespaceUri argument is used sparingly, because namespaces are difficult to manage from JavaScript. The doctype argument is rarely, if ever, used.

To create a new XML document with document element of <root>, you can use the following code:

image
var xmldom = document.implementation.createDocument("", "root", null);
                   
alert(xmldom.documentElement.tagName);  //"root"
                   
var child = xmldom.createElement("child");
xmldom.documentElement.appendChild(child);

DOMLevel2CoreExample01.htm

This example creates an XML DOM document with no default namespace and no doctype. Note that even though a namespace and doctype aren’t needed, the arguments must still be passed in. An empty string is passed as the namespace URI so that no namespace is applied, and null is passed as the doctype. The xmldom variable contains an instance of the DOM Level 2 Document type, complete with all of the DOM methods and properties discussed in Chapter 12. In this example, the document element’s tag name is displayed and then a new child element is created and added.

You can check to see if DOM Level 2 XML support is enabled in a browser by using the following line of code:

var hasXmlDom = document.implementation.hasFeature("XML", "2.0");

In practice, it is rare to create an XML document from scratch and then build it up systematically using DOM methods. It is much more likely that an XML document needs to be parsed into a DOM structure or vice versa. Because DOM Level 2 didn’t provide for such functionality, a couple of de facto standards emerged.

The DOMParser Type

Firefox introduced the DOMParser type specifically for parsing XML into a DOM document, and it was later adopted by Internet Explorer 9, Safari, Chrome, and Opera. To use it, you must first create an instance of DOMParser and then call the parseFromString() method. This method accepts two arguments: the XML string to parse and a content type, which should always be "text/xml". The return value is an instance of Document. Consider the following example:

var parser = new DOMParser();
var xmldom = parser.parseFromString("<root><child/></root>", "text/xml");
                   
alert(xmldom.documentElement.tagName);  //"root"
alert(xmldom.documentElement.firstChild.tagName); //"child"
                   
var anotherChild = xmldom.createElement("child");
xmldom.documentElement.appendChild(anotherChild);
                   
var children = xmldom.getElementsByTagName("child");
alert(children.length);   //2

DOMParserExample01.htm

In this example, a simple XML string is parsed into a DOM document. The DOM structure has <root> as the document element with a single <child> element as its child. You can then interact with the returned document using DOM methods.

The DOMParser can parse only well-formed XML and, as such, cannot parse HTML into an HTML document. Unfortunately, browsers behave differently when a parsing error occurs. When a parsing error occurs in Firefox, Opera, Safari, and Chrome, a Document object is still returned from parseFromString(), but its document element is <parsererror> and the content of the element is a description of the parsing error. Here is an example:

<parsererror xmlns="http://www.mozilla.org/newlayout/xml/parsererror.xml">XML Parsing Error: no element found Location: file:///I:/My%20Writing/My%20Books/Professional%20JavaScript/Second%20Edition/Examples/Ch15/DOMParserExample2.htm Line Number 1, Column 7:<sourcetext>&lt;root&gt; ------^</sourcetext></parsererror>

Firefox and Opera both return documents in this format. Safari and Chrome return a document that has a <parsererror> element embedded at the point where the parsing error occurred. Internet Explorer 9 throws a parsing error at the point where parseFromString() is called. Because of these differences, the best way to determine if a parsing error has occurred is to use a try-catch block, and if there’s no error, look for a <parsererror> element anywhere in the document via getElementsByTagName(), as shown here:

image
var parser = new DOMParser(),
    xmldom, 
    errors;
try {
    xmldom = parser.parseFromString("<root>", "text/xml");
    errors = xmldom.getElementsByTagName("parsererror");
    if (errors.length > 0){
        throw new Error("Parsing error!");
    }
} catch (ex) {
    alert("Parsing error!");
}

DOMParserExample02.htm

In this example, the string to be parsed is missing a closing </root> tag, which causes a parse error. In Internet Explorer 9+, this throws an error. In Firefox and Opera, the <parsererror> element will be the document element, whereas it’s the first child <root> in Chrome and Safari. The call to getElementsByTagName("parsererror") covers both cases. If any elements are returned by this method call, then an error has occurred and an alert is displayed. You could go one step further and extract the error information from the element as well.

The XMLSerializer Type

As a companion to DOMParser, Firefox also introduced the XMLSerializer type to provide the reverse functionality: serializing a DOM document into an XML string. Since that time, the XMLSerializer has been adopted by Internet Explorer 9+, Opera, Chrome, and Safari.

To serialize a DOM document, you must create a new instance of XMLSerializer and then pass the document into the serializeToString() method, as in this example:

image
var serializer = new XMLSerializer();
var xml = serializer.serializeToString(xmldom);
alert(xml);

XMLSerializerExample01.htm

The value returned from serializeToString() is a string that is not pretty-printed, so it may be difficult to read with the naked eye.

The XMLSerializer is capable of serializing any valid DOM object, which includes individual nodes and HTML documents. When an HTML document is passed into serializeToString(), it is treated as an XML document, and so the resulting code is well-formed.

image

If a non-DOM object is passed into the serializeToString() method, an error is thrown.

XML in Internet Explorer 8 and Earlier

Internet Explorer was actually the first browser to implement native XML processing support, and it did so through the use of ActiveX objects. Microsoft created the MSXML library to provide desktop-application developers with XML processing capabilities, and instead of creating different objects for JavaScript, they just enabled access to the same objects through the browser.

In Chapter 8, you were introduced to the ActiveXObject type, which is used to instantiate ActiveX objects in JavaScript. An XML document instance is created using the ActiveXObject constructor and passing in the string identifier for the XML document version. There are six different XML document objects, as described here:

  • Microsoft.XmlDom — Initial release with IE; should not be used.
  • MSXML2.DOMDocument — Updated version for scripting purposes but considered an emergency fallback only.
  • MSXML2.DOMDocument.3.0 — Lowest recommended version for JavaScript usage.
  • MSXML2.DOMDocument.4.0 — Not considered safe for scripting, so attempting to use it may result in a security warning.
  • MSXML2.DOMDocument 5.0 — Also not considered safe for scripting and may cause a security warning.
  • MSXML2.DOMDocument.6.0 — The most recent version marked safe for scripting.

Of the six versions, Microsoft recommends using only MSXML2.DOMDocument.6.0, which is the most recent and robust version, or MSXML2.DOMDocument.3.0, which is the version that is available on most Windows computers. The last fallback is MSXML2.DOMDocument, which may be necessary for browsers earlier than Internet Explorer 5.5.

You can determine which version is available by attempting to create each and watching for errors. For example:

image
function createDocument(){
    if (typeof arguments.callee.activeXString != "string"){
        var versions = ["MSXML2.DOMDocument.6.0", "MSXML2.DOMDocument.3.0",
                        "MSXML2.DOMDocument"],
            i, len;
                   
        for (i=0,len=versions.length; i < len; i++){
            try {
                new ActiveXObject(versions[i]);
                arguments.callee.activeXString = versions[i];
                break;
            } catch (ex){
                //skip
            }
        }
    }
                   
    return new ActiveXObject(arguments.callee.activeXString);
}

IEXmlDomExample01.htm

In this function, a for loop is used to iterate over the possible ActiveX versions. If the version isn’t available, the call to create a new ActiveXObject throws an error, in which case the catch statement catches the error and the loop continues. If an error doesn’t occur, then the version is stored as the activeXString property of the function, so that this process needn’t be repeated each time the function is called, and the created object is returned.

To parse an XML string, you must first create a DOM document and then call the loadXML() method. When the document is first created, it is completely empty and so cannot be interacted with. Passing an XML string into loadXML() parses the XML into the DOM document. Here’s an example:

var xmldom = createDocument();
xmldom.loadXML("<root><child/></root>");
                   
alert(xmldom.documentElement.tagName);  //"root"
alert(xmldom.documentElement.firstChild.tagName); //"child"
                   
var anotherChild = xmldom.createElement("child");
xmldom.documentElement.appendChild(anotherChild);
                   
var children = xmldom.getElementsByTagName("child");
alert(children.length);   //2        

IEXmlDomExample01.htm

Once the DOM document is filled with XML content, it can be interacted with just like any other DOM document, including all methods and properties.

Parsing errors are represented by the parseError property, which is an object with several properties relating to any parsing issues. These properties are as follows:

  • errorCode — Numeric code indicating the type of error that occurred, or 0 when there’s no error.
  • filePos — Position within the file where the error occurred.
  • line — The line on which the error occurred.
  • linepos — The character on the line where the error occurred.
  • reason — A plain text explanation of the error.
  • srcText — The code that caused the error.
  • url — The URL of the file that caused the error (if available).

The valueOf() method for parseError returns the value of errorCode, so you can check to see if a parsing error occurred by using the following:

if (xmldom.parseError != 0){
    alert("Parsing error occurred.");
}

An error code may be a positive or negative number, so you need only check to see if it’s not equal to 0. The details of the parsing error are easily accessible and can be used to indicate more useful error information, as shown in the following example:

image
if (xmldom.parseError != 0){
    alert("An error occurred:
Error Code: "
          + xmldom.parseError.errorCode + "
"
          + "Line: " + xmldom.parseError.line + "
"
          + "Line Pos: " + xmldom.parseError.linepos + "
"
          + "Reason: " + xmldom.parseError.reason);
}

IEXmlDomExample02.htm

You should check for parsing errors immediately after a call to loadXML() and before attempting to query the XML document for more information.

Serializing XML

XML serialization is built into the DOM document in Internet Explorer. Each node has an xml property that can be used to retrieve the XML string representing that node, as in this example:

alert(xmldom.xml);

This simple serialization method is available on every node in the document, allowing you to serialize the entire document or a specific subtree.

Loading XML Files

The XML document object in Internet Explorer can also load files from a server. As with the DOM Level 3 functionality, XML documents must be located on the same server as the page running the JavaScript code. Also similar to DOM Level 3, documents can be loaded synchronously or asynchronously. To determine which method to use, set the async property to either true or false (it’s true by default). Here’s an example:

var xmldom = createDocument();
xmldom.async = false;

Once you’ve determined the mode to load the XML document in, a call to load() initiates the download process. This method takes a single argument, which is the URL of the XML file to load. When run in synchronous mode, a call to load() can immediately be followed by a check for parsing errors and other XML processing, such as this:

image
var xmldom = createDocument();
xmldom.async = false;
xmldom.load("example.xml");
                   
if (xmldom.parseError != 0){
    //handle error
} else {
    alert(xmldom.documentElement.tagName);  //"root"
    alert(xmldom.documentElement.firstChild.tagName); //"child"
    
    var anotherChild = xmldom.createElement("child");
    xmldom.documentElement.appendChild(anotherChild);
    
    var children = xmldom.getElementsByTagName("child");
    alert(children.length);   //2        
    
    alert(xmldom.xml);        
}

IEXmlDomExample03.htm

Because the XML file is being processed synchronously, code execution is halted until the parsing is complete, allowing a simple coding procedure. Although this may be convenient, it could also lead to a long delay if the download takes longer than expected. XML documents are typically loaded asynchronously to avoid such issues.

When an XML file is loaded asynchronously, you need to assign an onreadystatechange event handler to the XML DOM document. There are four different ready states:

  • 1 — The DOM is loading data.
  • 2 — The DOM has completed loading the data.
  • 3 — The DOM may be used although some sections may not be available.
  • 4 — The DOM is completely loaded and ready to be used.

Practically speaking, the only ready state of interest is 4, which indicates that the XML file has been completely downloaded and parsed into a DOM. You can retrieve the ready state of the XML document via the readyState property. Loading an XML file asynchronously typically uses the following pattern:

image
var xmldom = createDocument();
xmldom.async = true;
                   
xmldom.onreadystatechange = function(){
    if (xmldom.readyState == 4){
        if (xmldom.parseError != 0){
            alert("An error occurred:
Error Code: "
                  + xmldom.parseError.errorCode + "
"
                  + "Line: " + xmldom.parseError.line + "
"
                  + "Line Pos: " + xmldom.parseError.linepos + "
"
                  + "Reason: " + xmldom.parseError.reason);
        } else {
            alert(xmldom.documentElement.tagName);  //"root"
            alert(xmldom.documentElement.firstChild.tagName); //"child"
            
            var anotherChild = xmldom.createElement("child");
            xmldom.documentElement.appendChild(anotherChild);
            
            var children = xmldom.getElementsByTagName("child");
            alert(children.length);   //2        
            
            alert(xmldom.xml);
        }
    }
};
                   
xmldom.load("example.xml");

IEXmlDomExample04.htm

Note that the assignment of the onreadystatechange event handler must happen before the call to load() to ensure that it gets called in time. Also note that inside of the event handler, you must use the name of the XML document variable, xmldom, instead of the this object. ActiveX controls disallow the use of this as a security precaution. Once the ready state of the document reaches 4, you can safely check to see if there’s a parsing error and begin your XML processing.

image

Even though it’s possible to load XML files via the XML DOM document object, it’s generally accepted to use an XMLHttpRequest for this instead. The XMLHttpRequest object, and Ajax in general, is discussed in Chapter 21.

Cross-Browser XML Processing

Since there are very few developers with the luxury of developing for a single browser, it’s frequently necessary to create browser-equalizing functions for XML processing. For XML parsing, the following function works in all of the major browsers:

image
function parseXml(xml){
    var xmldom = null;
    
    if (typeof DOMParser != "undefined"){
        xmldom = (new DOMParser()).parseFromString(xml, "text/xml");
        var errors = xmldom.getElementsByTagName("parsererror");
        if (errors.length){
            throw new Error("XML parsing error:" + errors[0].textContent);
        }                   
 
         
    } else if (typeof ActiveXObject != "undefined"){
        xmldom = createDocument();
        xmldom.loadXML(xml);
        if (xmldom.parseError != 0){
            throw new Error("XML parsing error: " + xmldom.parseError.reason);
        }
                   
    } else {
        throw new Error("No XML parser available.");
    }
    
    return xmldom;
}

CrossBrowserXmlExample01.htm

The parseXml() function accepts a single argument, the XML string to parse, and then uses capability detection to determine which XML parsing pattern to use. Since the DOMParser type is the most widely available solution, the function first tests to see if it is available. If so, a new DOMParser object is created and the XML string is parsed into the xmldom variable. Since DOMParser won’t throw an error for parsing errors other than in Internet Explorer 9+, the returned document is checked for errors, and if one is found, an error is thrown with the message.

The last part of the function checks for ActiveX support and uses the createDocument() function defined earlier to create an XML document using the correct signature. As with DOMParser, the result is checked for parsing errors. If one is found, then an error is thrown indicating the reported description.

If no XML parser is available, then the function simply throws an error indicating that it could not continue.

This function can be used to parse any XML string and should always be wrapped in a try-catch statement just in case a parsing error occurs. Here’s an example:

image
var xmldom = null;
                   
try {
    xmldom = parseXml("<root><child/></root>");
} catch (ex){
    alert(ex.message);
}
                   
//further processing

CrossBrowserXmlExample01.htm

For XML serialization, the same process can be followed to write a function that works in the four major browsers. For example:

function serializeXml(xmldom){
   
    if (typeof XMLSerializer != "undefined"){
        return (new XMLSerializer()).serializeToString(xmldom);
 
                   
    } else if (typeof xmldom.xml != "undefined"){
        return xmldom.xml;
                   
    } else {
        throw new Error("Could not serialize XML DOM.");
    }
}

CrossBrowserXmlExample02.htm

The serializeXml() function accepts a single argument, which is the XML DOM document to serialize. As with the parseXml() function, the first step is to check for the most widely available solution, which is XMLSerializer. If this type is available, then it is used to return the XML string for the document. Since the ActiveX approach simply uses the xml property, the function checks for that property specifically. If each of these three attempts fails, then the method throws an error indicating that serialization could not take place. Generally, serialization attempts shouldn’t fail if you’re using the appropriate XML DOM object for the browser, so it shouldn’t be necessary to wrap a call to serializeXml() in a try-catch. Instead, you can simply use this:

var xml = serializeXml(xmldom);

Note that because of differences in serializing logic, you may not end up with exactly the same serialization results from browser to browser.

XPATH SUPPORT IN BROWSERS

XPath was created as a way to locate specific nodes within a DOM document, so it’s important to XML processing. An API for XPath wasn’t part of a specification until DOM Level 3, which introduced the DOM Level 3 XPath recommendation. Many browsers chose to implement this specification, but Internet Explorer decided to implement support in its own way.

DOM Level 3 XPath

The DOM Level 3 XPath specification defines interfaces to use for evaluating XPath expressions in the DOM. To determine if the browser supports DOM Level 3 XPath, use the following JavaScript code:

var supportsXPath = document.implementation.hasFeature("XPath", "3.0");

Although there are several types defined in the specification, the two most important ones are XPathEvaluator and XPathResult. The XPathEvaluator is used to evaluate XPath expressions within a specific context. This type has the following three methods:

  • createExpression(expression, nsresolver) — Computes the XPath expression and accompanying namespace information into an XPathExpression, which is a compiled version of the query. This is useful if the same query is going to be run multiple times.
  • createNSResolver(node) — Creates a new XPathNSResolver object based on the namespace information of node. An XPathNSResolver object is required when evaluating against an XML document that uses namespaces.
  • evaluate(expression, context, nsresolver, type, result) — Evaluates an XPath expression in the given context and with specific namespace information. The additional arguments indicate how the result should be returned.

In Firefox, Safari, Chrome, and Opera, the Document type is typically implemented with the XPathEvaluator interface. So you can either create a new instance of XPathEvaluator or use the methods located on the Document instance (for both XML and HTML documents).

Of the three methods, evaluate() is the most frequently used. This method takes five arguments: the XPath expression, a context node, a namespace resolver, the type of result to return, and an XPathResult object to fill with the result (usually null, since the result is also returned as the function value). The third argument, the namespace resolver, is necessary only when the XML code uses an XML namespace. If namespaces aren’t used, this should be set to null. The fourth argument, the type of result to return, is one of the following 10 constants values:

  • XPathResult.ANY_TYPE — Returns the type of data appropriate for the XPath expression.
  • XPathResult.NUMBER_TYPE — Returns a number value.
  • XPathResult.STRING_TYPE — Returns a string value.
  • XPathResult.BOOLEAN_TYPE — Returns a Boolean value.
  • XPathResult.UNORDERED_NODE_ITERATOR_TYPE — Returns a node set of matching nodes, although the order may not match the order of the nodes within the document.
  • XPathResult.ORDERED_NODE_ITERATOR_TYPE — Returns a node set of matching nodes in the order in which they appear in the document. This is the most commonly used result type.
  • XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE — Returns a node set snapshot, capturing the nodes outside of the document so that any further document modification doesn’t affect the node set. The nodes in the node set are not necessarily in the same order as they appear in the document.
  • XPathResult.ORDERED_NODE_SNAPSHOT_TYPE — Returns a node set snapshot, capturing the nodes outside of the document so that any further document modification doesn’t affect the result set. The nodes in the result set are in the same order as they appear in the document.
  • XPathResult.ANY_UNORDERED_NODE_TYPE — Returns a node set of matching nodes, although the order may not match the order of the nodes within the document.
  • XPathResult.FIRST_ORDERED_NODE_TYPE — Returns a node set with only one node, which is the first matching node in the document.

The type of result you specify determines how to retrieve the value of the result. Here’s a typical example:

image
var result = xmldom.evaluate("employee/name", xmldom.documentElement, null, 
                                  XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
                   
if (result !== null) {
    var element = result.iterateNext();
    while(element) {
        alert(element.tagName);
        node = result.iterateNext();
    }
}

DomXPathExample01.htm

This example uses the XPathResult.ORDERED_NODE_ITERATOR_TYPE result, which is the most commonly used result type. If no nodes match the XPath expression, evaluate() returns null; otherwise, it returns an XPathResult object. The XPathResult has properties and methods for retrieving results of specific types. If the result is a node iterator, whether it be ordered or unordered, the iterateNext() method must be used to retrieve each matching node in the result. When there are no further matching nodes, iterateNext() returns null.

If you specify a snapshot result type (either ordered or unordered), you must use the snapshotItem() method and snapshotLength property, as in the following example:

var result = xmldom.evaluate("employee/name", xmldom.documentElement, null, 
                                  XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
if (result !== null) {
    for (var i=0, len=result.snapshotLength; i < len; i++) {
        alert(result.snapshotItem(i).tagName);
    }
}

DomXPathExample02.htm

In this example, snapshotLength returns the number of nodes in the snapshot, and snapshotItem() returns the node in a given position in the snapshot (similar to length and item() in a NodeList).

Single Node Results

The XPathResult.FIRST_ORDERED_NODE_TYPE result returns the first matching node, which is accessible through the singleNodeValue property of the result. For example:

image
var result = xmldom.evaluate("employee/name", xmldom.documentElement, null, 
                                  XPathResult.FIRST_ORDERED_NODE_TYPE, null);
                   
if (result !== null) {
    alert(result.singleNodeValue.tagName);
}

DomXPathExample03.htm

As with other queries, evaluate() returns null when there are no matching nodes. If a node is returned, it is accessed using the singleNodeValue property. This is the same for XPathResult.FIRST_ORDERED_NODE_TYPE.

Simple Type Results

It’s possible to retrieve simple, nonnode data types from XPath as well, using the XPathResult types of Boolean, number, and string. These result types return a single value using the booleanValue, numberValue, and stringValue properties, respectively. For the Boolean type, the evaluation typically returns true if at least one node matches the XPath expression and returns false otherwise. Consider the following:

var result = xmldom.evaluate("employee/name", xmldom.documentElement, null, 
                                  XPathResult.BOOLEAN_TYPE, null);
alert(result.booleanValue);

DomXPathExample04.htm

In this example, if any nodes match "employee/name", the booleanValue property is equal to true.

For the number type, the XPath expression must use an XPath function that returns a number, such as count(), which counts all the nodes that match a given pattern. Here’s an example:

var result = xmldom.evaluate("count(employee/name)", xmldom.documentElement, 
                                  null, XPathResult.NUMBER_TYPE, null);
alert(result.numberValue);

DomXPathExample05.htm

This code outputs the number of nodes that match "employee/name" (which is 2). If you try using this method without one of the special XPath functions, numberValue is equal to NaN.

For the string type, the evaluate() method finds the first node matching the XPath expression, and then returns the value of the first child node, assuming the first child node is a text node. If not, the result is an empty string. Here is an example:

image
var result = xmldom.evaluate("employee/name", xmldom.documentElement, null, 
                                  XPathResult.STRING_TYPE, null);
alert(result.stringValue);

DomXPathExample06.htm

In this example, the code outputs the string contained in the first text node under the first element matching "element/name".

Default Type Results

All XPath expressions automatically map to a specific result type. Setting the specific result type limits the output of the expression. You can, however, use the XPathResult.ANY_TYPE constant to allow the automatic result type to be returned. Typically, the result type ends up as a Boolean value, a number value, a string value, or an unordered node iterator. To determine which result type has been returned, use the resultType property on the evaluation result, as shown in this example:

var result = xmldom.evaluate("employee/name", xmldom.documentElement, null, 
                                  XPathResult.ANY_TYPE, null);
                   
if (result !== null) {
    switch(result.resultType) {
        case XPathResult.STRING_TYPE:
            //handle string type
            break;
                   
        case XPathResult.NUMBER_TYPE:
            //handle number type
            break;
                   
        case XPathResult.BOOLEAN_TYPE:
            //handle boolean type
            break;
                   
        case XPathResult.UNORDERED_NODE_ITERATOR_TYPE:
            //handle unordered node iterator type
            break;
                   
        default:
            //handle other possible result types
                   
    }
}

Using the XPathResult.ANY_TYPE constant allows more natural use of XPath but may also require extra processing code after the result is returned.

Namespace Support

For XML documents that make use of namespaces, the XPathEvaluator must be informed of the namespace information in order to make a proper evaluation. There are a number of ways to accomplish this. Consider the following XML code:

<?xml version="1.0" ?>
<wrox:books xmlns:wrox="http://www.wrox.com/">
    <wrox:book>
        <wrox:title>Professional JavaScript for Web Developers</wrox:title>
        <wrox:author>Nicholas C. Zakas</wrox:author>
    </wrox:book>
    <wrox:book>
        <wrox:title>Professional Ajax</wrox:title>
        <wrox:author>Nicholas C. Zakas</wrox:author>
        <wrox:author>Jeremy McPeak</wrox:author>
        <wrox:author>Joe Fawcett</wrox:author>
    </wrox:book>
</wrox:books>

In this XML document, all elements are part of the http://www.wrox.com/ namespace, identified by the wrox prefix. If you want to use XPath with this document, you need to define the namespaces being used; otherwise the evaluation will fail.

The first way to handle namespaces is by creating an XPathNSResolver object via the createNSResolver() method. This method accepts a single argument, which is a node in the document that contains the namespace definition. In the previous example, this node is the document element <wrox:books>, which has the xmlns attribute defining the namespace. This node can be passed into createNSResolver(), and the result can then be used in evaluate()as follows:

image
var nsresolver = xmldom.createNSResolver(xmldom.documentElement);
                   
var result = xmldom.evaluate("wrox:book/wrox:author", 
                             xmldom.documentElement, nsresolver,
                             XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
                   
alert(result.snapshotLength);

DomXPathExample07.htm

When the nsresolver object is passed into evaluate(), it ensures that the wrox prefix used in the XPath expression will be understood appropriately. Attempting to use this same expression without using an XPathNSResolver will result in an error.

The second way to deal with namespaces is by defining a function that accepts a namespace prefix and returns the associated URI, as in this example:

var nsresolver = function(prefix){
    switch(prefix){
        case "wrox": return "http://www.wrox.com/";
        //others here
    }
};
                   
var result = xmldom.evaluate("count(wrox:book/wrox:author)", 
                xmldom.documentElement, nsresolver, XPathResult.NUMBER_TYPE, null);
                   
alert(result.numberValue);

DomXPathExample08.htm

Defining a namespace-resolving function is helpful when you’re not sure which node of a document contains the namespace definitions. As long as you know the prefixes and URIs, you can define a function to return this information and pass it in as the third argument to evaluate().

XPath in Internet Explorer

XPath support is built into the ActiveX-based XML DOM document object in Internet Explorer but not with the DOM object returned from a DOMParser. In order to use XPath in Internet Explorer through version 9, you must use the ActiveX implementation. The interface defines two additional methods on every node: selectSingleNode() and selectNodes(). The selectSingleNode() method accepts an XPath pattern and returns the first matching node if found or null if there are no nodes. For example:

image
var element = xmldom.documentElement.selectSingleNode("employee/name");
                   
if (element !== null){
    alert(element.xml);
}

IEXPathExample01.htm

Here, the first node matching "employee/name" is returned. The context node is xmldom.documentElement, the node on which selectSingleNode() is called. Since it’s possible to get a null value returned from this method, you should always check to ensure that the value isn’t null before attempting to use node methods.

The selectNodes() method also accepts an XPath pattern as an argument, but it returns a NodeList of all nodes matching the pattern (if no nodes match, a NodeList with zero items is returned). Here is an example:

var elements = xmldom.documentElement.selectNodes("employee/name");
alert(elements.length);

IEXPathExample02.htm

In this example, all of the elements matching "employee/name" are returned as a NodeList. Since there is no possibility of a null value being returned, you can safely begin using the result. Remember that because the result is a NodeList, it is a dynamic collection that will constantly be updated every time it’s accessed.

XPath support in Internet Explorer is very basic. It’s not possible to get result types other than a node or NodeList.

Namespace Support in Internet Explorer

To deal with XPath expressions that contain namespaces in Internet Explorer, you’ll need to know which namespaces you’re using and create a string in the following format:

"xmlns:prefix1='uri1' xmlns:prefix2='uri2' xmlns:prefix3='uri3'"

This string then must be passed to a special method on the XML DOM document object in Internet Explorer called setProperty(), which accepts two arguments: the name of the property to set and the property value. In this case, the name of the property is "SelectionNamespaces", and the value is a string in the format mentioned previously. Therefore, the following code can be used to evaluate the XML document used in the DOM XPath namespaces example:

image
xmldom.setProperty("SelectionNamespaces", "xmlns:wrox='http://www.wrox.com/'");
                   
var result = xmldom.documentElement.selectNodes("wrox:book/wrox:author");            
alert(result.length);  

IEXPathExample03.htm

As with the DOM XPath example, failing to provide the namespace resolution information results in an error when the expression is evaluated.

Cross-Browser XPath

Since XPath functionality is so limited in Internet Explorer, cross-browser XPath usage must be kept to evaluations that Internet Explorer can execute. This means, essentially, recreating the selectSingleNode() and selectNodes() methods in other browsers using the DOM Level 3 XPath objects. The first function is selectSingleNode(), which accepts three arguments: the context node, the XPath expression, and an optional namespaces object. The namespaces object should be a literal in the following form:

{
    prefix1: "uri1",
    prefix2: "uri2",
    prefix3: "uri3"
}

Providing the namespace information in this way allows for easy conversion into the browser-specific namespace-resolving format. The full code for selectSingleNode() is as follows:

function selectSingleNode(context, expression, namespaces){
    var doc = (context.nodeType != 9 ? context.ownerDocument : context);
    
    if (typeof doc.evaluate != "undefined"){
        var nsresolver = null;
        if (namespaces instanceof Object){
            nsresolver = function(prefix){
                return namespaces[prefix];
            };
        }
        
        var result = doc.evaluate(expression, context, nsresolver, 
                                  XPathResult.FIRST_ORDERED_NODE_TYPE, null);
        return (result !== null ? result.singleNodeValue : null);
    
    } else if (typeof context.selectSingleNode != "undefined"){
                   
        //create namespace string
        if (namespaces instanceof Object){
            var ns = "";
            for (var prefix in namespaces){
                if (namespaces.hasOwnProperty(prefix)){
                    ns += "xmlns:" + prefix + "='" + namespaces[prefix] + "' ";
                }
            }
            doc.setProperty("SelectionNamespaces", ns);
        }
        return context.selectSingleNode(expression);
    } else {
        throw new Error("No XPath engine found.");
    }
}

CrossBrowserXPathExample01.htm

The first step in this function is to determine the XML document on which to evaluate the expression. Since a context node can be a document, it’s necessary to check the nodeType property. The variable doc holds a reference to the XML document after doing this check. At that point, you can check the document to see if the evaluate() method is present, indicating DOM Level 3 XPath support. If it is supported, the next step is to see if a namespaces object has been passed in. This is done by using the instanceof operator, because typeof returns "object" for null values and objects. The nsresolver variable is initialized to null and then overwritten with a function if namespace information is available. This function is a closure, using the passed-in namespaces object to return namespace URIs. After that, the evaluate() method is called and the result is inspected to determine whether or not a node was returned before returning a value.

The Internet Explorer branch of the function checks for the existence of the selectSingleNode() method on the context node. As with the DOM branch, the first step is to construct namespace information for the selection. If a namespaces object is passed in, then its properties are iterated over to create a string in the appropriate format. Note the use of the hasOwnProperty() method to ensure that any modifications to Object.prototype are not picked up by this function. The native selectSingleNode() method is then called and the result is returned.

If neither of the two methods is supported, then the function throws an error indicating that there’s no XPath engine available. The selectSingleNode() function can be used as follows:

image
var result = selectSingleNode(xmldom.documentElement, "wrox:book/wrox:author", 
                              { wrox: "http://www.wrox.com/" });            
alert(serializeXml(result));            

CrossBrowserXPathExample01.htm

A cross-browser selectNodes() function is created in a very similar fashion. The function accepts the same three arguments as the selectSingleNode() function and much of its logic is similar. For ease of reading, the following highlights the differences between the functions:

function selectNodes(context, expression, namespaces){
    var doc = (context.nodeType != 9 ? context.ownerDocument : context);
    
    if (typeof doc.evaluate != "undefined"){
        var nsresolver = null;
        if (namespaces instanceof Object){
            nsresolver = function(prefix){
                return namespaces[prefix];
            };
        }
        
        var result = doc.evaluate(expression, context, nsresolver, 
                                  XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
        var nodes = new Array();
        
        if (result !== null){
            for (var i=0, len=result.snapshotLength; i < len; i++){
                nodes.push(result.snapshotItem(i));
            }
        }
        
        return nodes;
    } else if (typeof context.selectNodes != "undefined"){
                   
        //create namespace string
        if (namespaces instanceof Object){
            var ns = "";
            for (var prefix in namespaces){
                if (namespaces.hasOwnProperty(prefix)){
                    ns += "xmlns:" + prefix + "='" + namespaces[prefix] + "' ";
                }
            }
            doc.setProperty("SelectionNamespaces", ns);
        }
        var result = context.selectNodes(expression);
        var nodes = new Array();
        
        for (var i=0,len=result.length; i < len; i++){
            nodes.push(result[i]);
        }
        
        return nodes;
    } else {
        throw new Error("No XPath engine found.");
    }
}

CrossBrowserXPathExample02.htm

As you can see, much of the same logic is used from selectSingleNode(). In the DOM portion of the code, an ordered snapshot result type is used and then stored in an array. To match the Internet Explorer implementation, the function should return an array even if no results were found, so the nodes array is always returned. In the Internet Explorer branch of the code, the selectNodes() method is called and then the results are copied into an array. Since Internet Explorer returns a NodeList, it’s best to copy the nodes over into an array, so the function returns the same type regardless of the browser being used. This function can then be used as follows:

image
var result = selectNodes(xmldom.documentElement, "wrox:book/wrox:author", 
                              { wrox: "http://www.wrox.com/" });            
alert(result.length);            

CrossBrowserXPathExample02.htm

For the best cross-browser compatibility, it’s best to use these two methods exclusively for XPath processing in JavaScript.

XSLT SUPPORT IN BROWSERS

XSLT is a companion technology to XML that makes use of XPath to transform one document representation into another. Unlike XML and XPath, XSLT has no formal API associated with it and is not represented in the formal DOM at all. This left browser vendors to implement support in their own way. The first browser to add XSLT processing in JavaScript was Internet Explorer.

XSLT in Internet Explorer

As with the rest of the XML functionality in Internet Explorer, XSLT support is provided through the use of ActiveX objects. Beginning with MSXML 3.0 (shipped with Internet Explorer 6), full XSLT 1.0 support is available via JavaScript. There is no XSLT support for DOM documents created using a DOMParser in Internet Explorer 9.

Simple XSLT Transformations

The simplest way to transform an XML document using an XSLT style sheet is to load each into a DOM document and then use the transformNode() method. This method exists on every node in a document and accepts a single argument, which is the document containing an XSLT style sheet. The transformNode() method returns a string containing the transformation. Here is an example:

image
//load the XML and XSLT (IE-specific)
xmldom.load("employees.xml");
xsltdom.load("employees.xslt");
                   
//transform
var result = xmldom.transformNode(xsltdom);

IEXsltExample01.htm

This example loads a DOM document with XML and a DOM document with the XSLT style sheet. Then, transformNode() is called on the XML document node, passing in the XSLT. The variable result is then filled with a string resulting from the transformation. Note that the transformation began at the document node level, because that’s where transformNode() was called. XSLT transformations can also take place anywhere in the document by calling transformNode() on the node at which you want the transformations to begin. Here is an example:

result = xmldom.documentElement.transformNode(xsltdom);
result = xmldom.documentElement.childNodes[1].transformNode(xsltdom);
result = xmldom.getElementsByTagName("name")[0].transformNode(xsltdom);
result = xmldom.documentElement.firstChild.lastChild.transformNode(xsltdom);

If you call transformNode() from anywhere other than the document element, you start the transformation at that spot. The XSLT style sheet, however, still has access to the full XML document from which that node came.

Complex XSLT Transformations

The transformNode() method gives basic XSLT transformation capabilities, but there are more complex ways to use the language. To do so, you must use an XSL template and an XSL processor. The first step is to load the XSLT style sheet into a thread-safe version of an XML document. This is done by using the MSXML2.FreeThreadedDOMDocument ActiveX object, which supports all of the same interfaces as a normal DOM document in Internet Explorer. This object needs to be created using the most up-to-date version as well. For example:

function createThreadSafeDocument(){
    if (typeof arguments.callee.activeXString != "string"){
        var versions = ["MSXML2.FreeThreadedDOMDocument.6.0", 
                        "MSXML2.FreeThreadedDOMDocument.3.0",
                        "MSXML2.FreeThreadedDOMDocument"],
            i, len;
                   
        for (i=0,len=versions.length; i < len; i++){
            try {
                new ActiveXObject(versions[i]);
                arguments.callee.activeXString = versions[i];
                break;
            } catch (ex){
                //skip
            }
        }
    }
                   
    return new ActiveXObject(arguments.callee.activeXString);
}

IEXsltExample02.htm

Aside from the different signature, using a thread-safe XML DOM document is the same as using the normal kind, as shown here:

var xsltdom = createThreadSafeDocument();
xsltdom.async = false;
xsltdom.load("employees.xslt");

After the free-threaded DOM document is created and loaded, it must be assigned to an XSL template, which is another ActiveX object. The template is used to create an XSL processor object that can then be used to transform an XML document. Once again, the most appropriate version must be created, like this:

image
function createXSLTemplate(){
    if (typeof arguments.callee.activeXString != "string"){
        var versions = ["MSXML2.XSLTemplate.6.0", 
                        "MSXML2.XSLTemplate.3.0",
                        "MSXML2.XSLTemplate"],
            i, len;
                   
        for (i=0,len=versions.length; i < len; i++){
            try {
                new ActiveXObject(versions[i]);
                arguments.callee.activeXString = versions[i];
                break;
            } catch (ex){
                //skip
            }
        }
    }
                   
    return new ActiveXObject(arguments.callee.activeXString);
}

IEXsltExample02.htm

You can use the createXSLTemplate() function to create the most recent version of the object, as in this example:

var template = createXSLTemplate();
template.stylesheet = xsltdom;
                   
var processor = template.createProcessor();
processor.input = xmldom;
processor.transform();
                   
var result = processor.output;

IEXsltExample02.htm

When the XSL processor is created, the node to transform must be assigned to the input property. This value may be a document or any node within a document. The call to transform() executes the transformations and stores the result in the output property as a string. This code duplicates the functionality available with transformNode().

image

There is a significant difference between the 3.0 and 6.0 versions of the XSL template object. In 3.0, the input property must be a complete document; using a node throws an error. In 6.0, you may use any node in a document.

Using the XSL processor allows extra control over the transformation and provides support for more advanced XSLT features. For example, XSLT style sheets accept parameters that can be passed in and used as local variables. Consider the following style sheet:

image
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 
    <xsl:output method="html" />
 
    <xsl:param name="message" />
        
    <xsl:template match="/">
       <ul>
           <xsl:apply-templates select="*" />
       </ul>
       <p>Message: <xsl:value-of select="$message" /></p>
    </xsl:template>
 
    <xsl:template match="employee">
        <li><xsl:value-of select="name" />, <em><xsl:value-of select="@title" /></em></li>
    </xsl:template>
 
</xsl:stylesheet>

employees.xslt

This style sheet defines a parameter named message and then outputs that parameter into the transformation result. To set the value of message, you use the addParameter() method before calling transform(). The addParameter() method takes two arguments: the name of the parameter to set (as specified in <xsl:param>’s name attribute) and the value to assign (most often a string, but it can be a number or Boolean as well). Here is an example:

image
processor.input = xmldom.documentElement;
processor.addParameter("message", "Hello World!");
processor.transform();

IEXsltExample03.htm

When you set a value for the parameter, the output will reflect the value.

Another advanced feature of the XSL processor is the capability to set a mode of operation. In XSLT, it’s possible to define a mode for a template using the mode attribute. When a mode is defined, the template isn’t run unless <xsl:apply-templates> is used with a matching mode attribute. Consider the following example:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 
    <xsl:output method="html" />
 
    <xsl:param name="message" />
 
    <xsl:template match="/">
       <ul>
           <xsl:apply-templates select="*" />
       </ul>
       <p>Message: <xsl:value-of select="$message" /></p>
    </xsl:template>
 
    <xsl:template match="employee">
        <li><xsl:value-of select="name" />, <em><xsl:value-of select="@title" /></em></li>
    </xsl:template>
 
    <xsl:template match="employee" mode="title-first">
        <li><em><xsl:value-of select="@title" /></em>, <xsl:value-of select="name" /></li>
    </xsl:template>
 
</xsl:stylesheet>

employees3.xslt

This style sheet defines a template with its mode attribute set to "title-first". Inside of this template, the employee’s title is output first, and the employee name is output second. In order to use this template, the <xsl:apply-templates> element must have its mode set to "title-first" as well. If you use this style sheet, it has the same output as the previous one by default, displaying the employee name first and the position second. If, however, you use this style sheet and set the mode to "title-first" using JavaScript, it outputs the employee’s title first. This can be done in JavaScript using the setStartMode() method as shown here:

image
processor.input = xmldom;
processor.addParameter("message", "Hello World!");  
processor.setStartMode("title-first");              
processor.transform();  

IEXsltExample05.htm

The setStartMode() method accepts only one argument, which is the mode to set the processor to. Just as with addParameter(), this must be called before transform().

If you are going to do multiple transformations using the same style sheet, you can reset the processor after each transformation. When you call the reset() method, the input and output properties are cleared, as well as the start mode and any specified parameters. The syntax for this method is as follows:

processor.reset();   //prepare for another use

Because the processor has compiled the XSLT style sheet, it is faster to make repeat transformations versus using transformNode().

image

MSXML supports only XSLT 1.0. Development on MSXML has stopped since Microsoft’s focus has shifted to the .NET Framework. It is expected that, at some point in the future, JavaScript will have access to the XML and XSLT .NET objects.

The XSLTProcessor Type

Mozilla implemented JavaScript support for XSLT in Firefox by creating a new type. The XSLTProcessor type allows developers to transform XML documents by using XSLT in a manner similar to the XSL processor in Internet Explorer. Since it was first implemented, Chrome, Safari, and Opera have copied the implementation, making XSLTProcessor into a de facto standard for JavaScript-enabled XSLT transformations.

As with the Internet Explorer implementation, the first step is to load two DOM documents, one with the XML and the other with the XSLT. After that, create a new XSLTProcessor and use the importStylesheet() method to assign the XSLT to it, as shown in this example:

var processor = new XSLTProcessor()
processor.importStylesheet(xsltdom);

XsltProcessorExample01.htm

The last step is to perform the transformation. This can be done in two different ways. If you want to return a complete DOM document as the result, call transformToDocument(). You can also get a document fragment object as the result by calling transformToFragment(). Generally speaking, the only reason to use transformToFragment() is if you intend to add the results to another DOM document.

When using transformToDocument(), just pass in the XML DOM and use the result as another completely different DOM. Here’s an example:

image
var result = processor.transformToDocument(xmldom);
alert(serializeXml(result));

XsltProcessorExample01.htm

The transformToFragment() method accepts two arguments: the XML DOM to transform and the document that should own the resulting fragment. This ensures that the new document fragment is valid in the destination document. You can, therefore, create the fragment and add it to the page by passing in document as the second argument. Consider the following example:

var fragment = processor.transformToFragment(xmldom, document);
var div = document.getElementById("divResult");
div.appendChild(fragment);

XsltProcessorExample02.htm

Here, the processor creates a fragment owned by the document object. This enables the fragment to be added to a <div> element that exists in the page.

When the output format for an XSLT style sheet is either "xml" or "html", creating a document or document fragment makes perfect sense. When the output format is "text", however, you typically just want the text result of the transformation. Unfortunately, there is no method that returns text directly. Calling transformToDocument() when the output is "text" results in a full XML document being returned, but the contents of that document are different from browser to browser. Safari, for example, returns an entire HTML document, whereas Opera and Firefox return a one-element document with the output as the element’s text.

The solution is to call transformToFragment(), which returns a document fragment that has a single child node containing the result text. You can, therefore, get the text by using the following code:

var fragment = processor.transformToFragment(xmldom, document);
var text = fragment.firstChild.nodeValue;
alert(text);

This code works the same way for each of the supporting browsers and correctly returns just the text output from the transformation.

Using Parameters

The XSLTProcessor also allows you to set XSLT parameters using the setParameter() method, which accepts three arguments: a namespace URI, the parameter local name, and the value to set. Typically, the namespace URI is null, and the local name is simply the parameter’s name. This method must be called prior to transformToDocument() or transformToFragment(). Here’s an example:

image
var processor = new XSLTProcessor()
processor.importStylesheet(xsltdom);
processor.setParameter(null, "message", "Hello World!");
var result = processor.transformToDocument(xmldom);

XsltProcessorExample03.htm

There are two other methods related to parameters, getParameter() and removeParameter(), which are used to get the current value of a parameter and remove the parameter value, respectively. Each method takes the namespace URI (once again, typically null) and the local name of the parameter. For example:

var processor = new XSLTProcessor()
processor.importStylesheet(xsltdom);
processor.setParameter(null, "message", "Hello World!");
                   
alert(processor.getParameter(null, "message"));    //outputs "Hello World!"
processor.removeParameter(null, "message");
                   
var result = processor.transformToDocument(xmldom);

These methods aren’t used often and are provided mostly for convenience.

Resetting the Processor

Each XSLTProcessor instance can be reused multiple times for multiple transformations with different XSLT style sheets. The reset() method removes all parameters and style sheets from the processor, allowing you to once again call importStylesheet() to load a different XSLT style sheet, as in this example:

var processor = new XSLTProcessor()
processor.importStylesheet(xsltdom);
                   
//do some transformations
                   
processor.reset();
processor.importStylesheet(xsltdom2);
                   
//do more transformations

Reusing a single XSLTProcessor saves memory when using multiple style sheets to perform transformations.

Cross-Browser XSLT

The Internet Explorer XSLT transformation is quite different from the XSLTProcessor approach, so recreating all of the functionality available in each is not realistic. The easiest cross-browser technique for XSLT transformations is to return a string result. For Internet Explorer, this means simply calling transformNode() on the context node, whereas other browsers need to serialize the result of a transformToDocument() operation. The following function can be used in Internet Explorer, Firefox, Chrome, Safari, and Opera:

image
function transform(context, xslt){
    if (typeof XSLTProcessor != "undefined"){
        var processor = new XSLTProcessor();
        processor.importStylesheet(xslt);
                   
        var result = processor.transformToDocument(context);
        return (new XMLSerializer()).serializeToString(result);
                   
    } else if (typeof context.transformNode != "undefined") {
        return context.transformNode(xslt);
    } else {
        throw new Error("No XSLT processor available.");
    }
}

CrossBrowserXsltExample01.htm

The transform() function accepts two arguments: the context node on which to perform the transformation and the XSLT document object. First, the code checks to see if the XSLTProcessor type is defined, and if so, it uses that to process the transformation. The transformToDocument() method is called and the result is serialized into a string to be returned. If the context node has a transformNode() method, then that is used to return the result. As with the other cross-browser functions in this chapter, transform() throws an error if there is no XSLT processor available. This function is used as follows:

var result = transform(xmldom, xsltdom);

Using the Internet Explorer transformNode() method ensures that you don’t need to use a thread-safe DOM document for the transformation.

image

Note that because of different XSLT engines in browsers, the results you receive from a transformation may vary slightly or greatly from browser to browser. You should never depend on an absolute transformation result using XSLT in JavaScript.

SUMMARY

There is a great deal of support for XML and related technologies in JavaScript. Unfortunately, because of an early lack of specifications, there are several different implementations for common functionality. DOM Level 2 provides an API for creating empty XML documents but not for parsing or serialization. Because of this lack of functionality, browser vendors began creating their own approaches. Internet Explorer took the following approach:

  • Internet Explorer introduced XML support through ActiveX objects, the same objects that could be used to build desktop applications.
  • The MSXML library ships with Windows and is accessible from JavaScript.
  • This library includes support for basic XML parsing and serialization and for complementary technologies such as XPath and XSLT.

Firefox, on the other hand, implemented two new types to deal with XML parsing and serialization as follows:

  • The DOMParser type is a simple object that parses an XML string into a DOM document.
  • The XMLSerializer type performs the opposite operation, serializing a DOM document into an XML string.

Because of the simplicity and popularity of these objects, Internet Explorer 9, Opera, Chrome, and Safari duplicated the functionality, and these types are de facto standards in web development.

DOM Level 3 introduced a specification for an XPath API that has been implemented by Firefox, Safari, Chrome, and Opera. The API enables JavaScript to run any XPath query against a DOM document and retrieve the result regardless of its data type. Internet Explorer implemented its own XPath support in the form of two methods: selectSingleNode() and selectNodes(). Although much more limited than the DOM Level 3 API, these methods provide basic XPath functionality to locate a node or set of nodes in a DOM document.

The last related technology is XSLT, which has no public specification defining an API for its usage. Firefox created the XSLTProcessor type to handle transformations via JavaScript and was soon copied by Safari, Chrome, and Opera. Internet Explorer implemented its own solution, both with the simple transformNode() method and through a more complicated template/processor approach.

XML is now well supported in Internet Explorer, Firefox, Chrome, Safari, and Opera. Even though the implementations vary wildly between Internet Explorer and the other browsers, there’s enough commonality to create reasonable cross-browser functionality.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.157.34