A few weeks ago I was looking for a solution to parse an XML file into XHTML. This can be helpful if you want to store data for your web site in XML and want a quick scripting language to access it and parse it, this technique can be used to parse RSS since it is XML.
I spent a couple of hours scouring the web for information. I was able to gather bits and pieces from different ends of the Internet to come up with a solution.
First let’s look at the XML document. The data in my XML file contains multiple choice questions and their answers. This means I will need to parse out the data and form a label for the question and radio buttons for the answers. Nothing fancy here, my question node has a number attribute and a text attribute. The answer nodes have a text attribute as well, and a value attribute to determine whether the radio button should be checked by default.
The XML looks like:
<?xml version="1.0" encoding="iso-8859-1"?>
<questions>
<question number="1" text="How painful is it to parse XML with JavaScript?">
<option value="Y" text="Ungodly painful"></option>
<option value="N" text="Extremely painful"></option>
<option value="N" text="Moderately painful"></option>
<option value="N" text="Mildly painful"></option>
</question>
<question number="2" text="How retarded is JScript?">
<option value="N" text="Poo flinging retarded"></option>
<option value="N" text="Extremely retarded"></option>
<option value="Y" text="Moderately retarded"></option>
<option value="N" text="Mildly retarded"></option>
</question>
</questions>
On to coding, you need to determine what sort of browser the user will be using to view your parsed XML. The function I found to check the browser and load the XML appropriately, checks for Internet Explorer and non-evil-empire browsers, Firefox, Opera, etc.
Internet Explorer uses a different type of type of JavaScript when parsing a web page. Without boring you about the details, the basic difference is Microsoft uses a custom version of JavaScript called JScript, leave it to Microsoft to make life hard on us all.
I gathered the load XML data function from w3schools.
Here’s the function to check the browser and load the XML document:
var xmlDoc;
var ELEMENT_NODE = 1;
function loadXmlData()
{
if (window.ActiveXObject)
{
xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async=false;
xmlDoc.load("http://sbiefeld.com/wp-content/uploads/2008/03/questions.xml");
writeListForPOSBrowsers();
}
else if (document.implementation && document.implementation.createDocument)
{
xmlDoc = document.implementation.createDocument("", "", null);
xmlDoc.onload = writeListForRealBrowsers;
xmlDoc.load("http://sbiefeld.com/wp-content/uploads/2008/03/questions.xml");
}
else
{
alert('Your browser cannot handle this script');
}
}
Notice the different function calls per browser, writeListForPOSBrowsers() and writeListForRealBrowsers(). This was to handle the peculiarities of IE and JScript. The writeListForPOSBrowsers() function handles the parsing for IE while the writeListForRealBrowsers() function handles the parsing for other browsers.
I guess I will start with the writeListForPOSBrowsers() first and save the best for last. The basic flow is: loop through the question nodes in the XML file and pull out question attributes and parse them into HTML elements. For each question node, I loop through the answer nodes to and create the radio buttons for the answer choices.
The most frustrating thing is the lack of support for the DOM that JScript has. Instead of being able to reference HTML elements and append data accordingly I had to manually put my HTML elements in a string and then append to that string. Once everything is in place, I put that HTML string in the document. See comments in code for more specifics.
function writeListForPOSBrowsers()
{
var questions = xmlDoc.getElementsByTagName('question');
var htmlCode = '<ul>';
for (i=0; i < questions.length; i++)
{
var questionId = questions[i].getAttribute('number');
htmlCode += '<li>'+'(' + questionId + ') ';
if (questions[i].nodeType != ELEMENT_NODE) continue;
var cdata = questions[i].getAttribute('text');
htmlCode += cdata + '</li>';
for (j=0; j < questions[i].childNodes.length; j++)
{
if(j!=0){htmlCode += '<br/>';}
var inputId = questionId + '_' + j;
if (questions[i].childNodes[j].nodeType != ELEMENT_NODE) continue;
var rbtnTxt = questions[i].childNodes[j].getAttribute('text');
var isChkd = questions[i].childNodes[j].getAttribute('value');
var isChkdValue;
if(isChkd.toUpperCase() == 'Y')
{
isChkdValue = 'checked';
}
else
{
isChkdValue = '';
}
htmlCode += '<input type="radio" id="' + inputId + '" name="question'+i+'" '+isChkdValue+' value="'+ rbtnTxt+'"';
htmlCode += '<label>'+rbtnTxt+'</label>';
}
htmlCode += '<br/><br/>';
}
document.getElementById('updateTarget').innerHTML = htmlCode;
}
Now, the best for last. The function to parse XML for non-IE browsers. In this function I could use the DOM, w00t for strongly typed objects, well kind of, since JavaScript is inherently not typed at all, but that is another topic.
I use exactly the same process as before loop through the question nodes and for each loop though its answer nodes. Then append the parsed items to the page.
function writeListForRealBrowsers()
{
var questions = xmlDoc.getElementsByTagName('question');
var ul = document.createElement('ul');
for (i=0; i < questions.length; i++)
{
var li = document.createElement('li');
var brk = document.createElement('br');
var questionId = questions[i].getAttribute('number');
var questoinNum = document.createTextNode('(' + questionId + ') ');
li.appendChild(questoinNum);
ul.appendChild(li);
if (questions[i].nodeType != ELEMENT_NODE) continue;
var cdata = document.createTextNode(questions[i].getAttribute('text'));
li.appendChild(cdata);
li.appendChild(brk);
for (j=0; j < questions[i].childNodes.length; j++)
{
var brk = document.createElement('br');
var inpt = document.createElement('input');
var lbl = document.createElement('label');
inpt.type = 'radio';
var inputId = questionId + '_' + j;
inpt.id = inputId;
inpt.name = 'question' + i;
if (questions[i].childNodes[j].nodeType != ELEMENT_NODE) continue;
var rbtnTxt = questions[i].childNodes[j].getAttribute('text');
var isChkd = questions[i].childNodes[j].getAttribute('value');
inpt.checked = (isChkd.toUpperCase() == 'Y');
inpt.value = rbtnTxt;
lbl.appendChild(document.createTextNode(rbtnTxt));
li.appendChild(inpt);
li.appendChild(lbl);
li.appendChild(brk);
}
}
document.getElementById('updateTarget').appendChild(ul);
}
Last but not least, the HTML to use the javascript:
<body onLoad="loadXmlData()" id='updateTarget'></body>
Simple enough, right? Well I hope my gatherings of how to parse XML with Javascript has been useful and helpful. This may by no means be the best way to do it, and it could be re-factored. It does however stay true to the title, Quick and Dirty XML Parsing with JavaScript. Grab the XML file and grab and test the HTML/JavaScript. Thanks much and expect more.