How can I use JavaScript to transform XML & XSLT?

I want to use JavaScript to show my XSLT, but on my server nothing is showing up in the browser.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js"></script>
<script type="text/javascript" src="main.js"></script>
<meta http-equiv="Content-Language" content="en-us"/>
<title>Contracting, Licensing and Compliance News</title>
</head>
<body>
<script language="javascript">
    function displayMessage() {
        // Load XML 

        var xml = new ActiveXObject("Microsoft.XMLDOM")
        xml.async = false
        xml.load("site-index.xml")

        // Load the XSL
        var xsl = new ActiveXObject("Microsoft.XMLDOM")
        xsl.async = false
        xsl.load("site-index.xsl")

        // Transform
        document.write(xml.transformNode(xsl))
    }
</script>
</body>
</html>

Answers


The browser can perform the tranform for you. No javascript is needed, just link the .xsl from the .xml like this:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="site-index.xsl" ?>

and just serve the xml, no html. Assuming your .xsl contains the right code, along the lines

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" version="4.01" indent="yes"/>
<xsl:output doctype-system="http://www.w3.org/TR/html4/strict.dtd"/>
<xsl:output doctype-public="-//W3C//DTD HTML 4.01//EN"/>

You are probably better off allowing the browser to perform the transformation using the method Xenan outlines. However, it is perfectly possible to do this in JavaScript as well. Here is an outline of how you might accomplish this in a cross-browser manner.

First, you will need to load the XML and XSL. There are many ways of doing this. Usually, it will involve some sort of AJAX, but not necessarily. To keep this answer simple, I will assume you have this part covered, but please let me know if you need more help, and I will edit to include an example of loading XML.

Therefore, let us assume we have these objects:

var xml, xsl;

Where xml contains an XML structure, and xsl contains the stylesheet that you wish to transform with.


Edit:

If you need to load those objects, you will end up using some form of AJAX to do so. There are many examples of cross-browser AJAX out there. You will be better off using a library to accomplish this, rather than rolling your own solution. I suggest you look into jquery or YUI, both of which do an excellent job of this.

However, the basic idea is pretty simple. To complete this answer, here is some non-library specific code that accomplishes this in a cross-browser manner:

function loadXML(path, callback) {
    var request;

    // Create a request object. Try Mozilla / Safari method first.
    if (window.XMLHttpRequest) {
        request = new XMLHttpRequest();

    // If that doesn't work, try IE methods.
    } else if (window.ActiveXObject) {
        try {
            request = new ActiveXObject("Msxml2.XMLHTTP");
        } catch (e1) {
            try {
                request = new ActiveXObject("Microsoft.XMLHTTP");
            } catch (e2) {
            }
        }
    }

    // If we couldn't make one, abort.
    if (!request) {
        window.alert("No ajax support.");
        return false;
    }

    // Upon completion of the request, execute the callback.
    request.onreadystatechange = function () {
        if (request.readyState === 4) {
            if (request.status === 200) {
                callback(request.responseXML);
            } else {
                window.alert("Could not load " + path);
            }
        }
    };

    request.open("GET", path);
    request.send();
}

You would use this code by giving it a path to your XML, and a function to execute when loading is complete:

loadXML('/path/to/your/xml.xml', function (xml) {
    // xml contains the desired xml document.
    // do something useful with it!
});

I have updated my example to show this technique. You can find some working demonstration code here.


To perform a transformation, you will end up with a third XML document, which is the result of that transformation. If you are working with IE, you use the "transformNodeToObject" method, and if you are working with other browsers, you use the "transformToDocument" method:

var result;

// IE method
if (window.ActiveXObject) {
    result = new ActiveXObject("MSXML2.DOMDocument");
    xml.transformNodeToObject(xsl, result);

// Other browsers
} else {
    result = new XSLTProcessor();
    result.importStylesheet(xsl);
    result = result.transformToDocument(xml);
}

At this point, result should contain the resulting transformation. This is still an XML document, and you should treat it as such. If you want a string which you can pass into document.write or innerHTML, you have a little more work to do.

Once again, there is an IE method for this, and a method that applies to other browsers.

var x, ser, s = '';

// IE method.
if (result.childNodes[0] && result.childNodes[0].xml) {
    for (x = 0; x < result.childNodes.length; x += 1) {
        s += result.childNodes[x].xml;
    }
// Other browsers
} else {
    ser = new XMLSerializer();
    for (x = 0; x < result.childNodes.length; x += 1) {
        s += ser.serializeToString(result.childNodes[x]);
    }
}

Now s should contain the resulting XML as a string. You should be able to pass this into document.write or innerHTML and have it do something useful. Note that it may contain an XML declaration, which you might want to strip out, or not.

I've tested this in Chrome, IE9, and FF4. You can find a simplified, barebones, working example of this in my testbed.

Good luck and happy coding!


Use this script to transform test.xml using test.xsl and append the output to container.

 <div id="container"></div>
 <script>
     function loadXMLDoc(filename) {
        if (window.ActiveXObject) {
             xhttp = new ActiveXObject("Msxml2.XMLHTTP");
        } else {
             xhttp = new XMLHttpRequest();
        }
        xhttp.open("GET", filename, false);
        xhttp.send("");
        return xhttp.responseXML;
     }

     xml = loadXMLDoc("test.xml");
     xsl = loadXMLDoc("test.xsl");
     if (document.implementation && document.implementation.createDocument) {
        xsltProcessor = new XSLTProcessor();
        xsltProcessor.importStylesheet(xsl);
        resultDocument = xsltProcessor.transformToFragment(xml, document);
        document.getElementById('container').appendChild(resultDocument);
     }
 </script>

This works in Chrome/Firefox/Edge/IE11

 function loadXMLDoc(filename) {
     if (window.ActiveXObject || "ActiveXObject" in window) {
         xhttp = new ActiveXObject("Msxml2.XMLHTTP");
     } else {
         xhttp = new XMLHttpRequest();
     }
     xhttp.open("GET", filename, false);
     xhttp.send("");
     return xhttp.responseXML;
 }


 if (window.ActiveXObject || "ActiveXObject" in window) {
     ie();
 } else {

     xml = loadXMLDoc("input.xml");
     xsl = loadXMLDoc("mmlctop2_0.xsl");

     if (document.implementation && document.implementation.createDocument) {
         xsltProcessor = new XSLTProcessor();
         xsltProcessor.importStylesheet(xsl);
         resultDocument = xsltProcessor.transformToDocument(xml, document);

         var serializer = new XMLSerializer();
         var transformed = serializer.serializeToString(resultDocument.documentElement);

         alert(transformed);
     }
 }

 // https://msdn.microsoft.com/en-us/library/ms753809(v=vs.85).aspx
 function ie() {

     var xslt = new ActiveXObject("Msxml2.XSLTemplate.3.0");
     var xslDoc = new ActiveXObject("Msxml2.FreeThreadedDOMDocument.3.0");
     var xslProc;
     xslDoc.async = false;
     xslDoc.load("mmlctop2_0.xsl");
     if (xslDoc.parseError.errorCode != 0) {
         var myErr = xslDoc.parseError;
         alert("You have error " + myErr.reason);
     } else {
         xslt.stylesheet = xslDoc;
         var xmlDoc = new ActiveXObject("Msxml2.DOMDocument.3.0");
         xmlDoc.async = false;
         xmlDoc.load("input.xml");
         if (xmlDoc.parseError.errorCode != 0) {
             var myErr = xmlDoc.parseError;
             alert("You have error " + myErr.reason);
         } else {
             xslProc = xslt.createProcessor();
             xslProc.input = xmlDoc;
             xslProc.addParameter("param1", "Hello");
             xslProc.transform();
             alert(xslProc.output);
         }
     }


 }

Just a comment in a form of an answer due to low reputation. if you get the documents via AJAX call, DO return the xhttp.responseXML and do NOT return xhttp.responseXML.documentElement.

Both can be converted using XMLSerializer to a meaningful representation of the document but only the first statement is a viable parameter for the XSL Transformation.

If the latter is used the return value of the XSLTransformation (using either toDocument or toFragment function) is null in my case (using Chrome)

For that matter, I don't believe Chrome has dropped XSLT support as stated somewhere on the page here.

Hope that helps


Need Your Help

class A declares multiple JSON fields

java json gson

i have a class A which has some private fields and the same class extends another class B which also has some private fields which are in class A.

Allow User to Cancel MBProgressHUD when JSON call takes too long

ios xcode json uigesturerecognizer mbprogresshud

I've read and read on SO about this, and I just can't seem to find anything that matches my situation.