Helpful Information
 
 
Category: Ajax and Design
XML to XHTML Transformation

In the past whenever I needed to get a bit of data from the server through an xmlHTTPRequest, I could do one of two things:

Serve my self up the data in xml and then parse through each xml tag and create an appropriate html tag. In turn appending any applicable data to each html element. -- Big hassle but imho the right way.

The other way (the dirty way) to just get the responseText and change the innerHTML of my target element. -- Nice and easy but dirty.

Now I thought, it sure would be nice if there was a responseXHTML that I could traverse to get the top node of the xhtml that I am interested in and simply append that to my target dom node.

There is no such thing that I am aware of, but I've come up with the following that attempts to do just that. It all seems to be working for the most part, however, it's appending an undefined element to the target node, it is unaparent when rendered in the browser window, but in the DOM inspector it is viewable. I'd also appreciate any feedback on things I may not have taken into consideration.


Below is the code that parses the XML:


function toXHTML(obj){
var div=document.createElement("div");
makeChild(obj,div);
function makeChild(src_xml,targ_html){
var new_html=transformXML(src_xml);
targ_html.appendChild(new_html);
var kids=src_xml.childNodes;
var len=kids.length;
for(var i=0;i<len;i++){
makeChild(kids[i],new_html);
}
}
return div;
}
function transformXML(obj){
if(obj.tagName){
var ie = (document.all && !window.opera)?1:0;
var flg=0;
if(ie){
if(obj.tagName.toLowerCase()=="input"){
if(obj.getAttribute('type').toLowerCase()=="radio"||obj.getAttribute('type').toLowerCase()=="checkbox"){
var flg=1;
var html_obj=document.createElement('<'+obj.tagName+' name='+obj.getAttribute("name")+'" />');
}else{
var html_obj=document.createElement(obj.tagName);
}
}else{
var html_obj=document.createElement(obj.tagName);
}
}else{
var html_obj=document.createElement(obj.tagName);
}
var atts=obj.attributes;
var len=atts.length;
for(var i=0;i<len;i++){
switch(atts[i].name.toLowerCase()){
case "name":
if(flg==0){
html_obj.name=atts[i].value;
}
break;
case "class":
html_obj.className=atts[i].value;
break;
case "value":
html_obj.value=atts[i].value;
break;
case "colspan":
html_obj.setAttribute("colSpan",atts[i].value);
break;
default:
html_obj.setAttribute(atts[i].name.toLowerCase(),atts[i].value);
}
}
}else{
html_obj=document.createTextNode(obj.nodeValue);
}
return html_obj;
}


Please note if you try to use this, it isn't expecting event handlers in your tags, event handlers can be added from script on the calling page.

Just return well-formed XHTML markup to the request, then you can do a DOM traversal to the node you want, call it myNode, and then say

myNode = document.importNode(myNode, true);
so that you can append it (some browsers don't require importNode, and may in fact throw an error about it... something to detect)) directly into the current document. No need to parse anything.

Ha! That's awesome, now I wish I didn't take the day off today. So you are saying importNode will make it so when I append the xml node, which is xhtml, it will treat it as html and display properly in the page? If so, that's gonna make things way easier. :)

I don't think IE supports importNode unfortunately.

david_kw

I know this has nothing really innovative in it but to keep things abstracted I have done something like the below example. This way you use the browsers parser and don't have to recreate it. The code, of course, has to be more dynamic if for some reason the stringXHTML doesn't have a rootNode.



function createXHTMLNode(stringXHTML) {
var div = document.createElement("div");
div.innerHTML = stringXHTML;

return(div.firstChild);
}

var rootNode = createXHTMLNode(xhr.responseText);
document.getElementById("mydiv").appendChild(rootNode);


Then you could swap out there "right" way in function createXML with importNode if it is there or whatever else makes sense for your data. It is of course identical to setting the innerHTML of mydiv but it makes me feel better about using it. :)

david_kw

Yeah, it doesn't seem to work in IE. So it seems I still have use for my code above. Any ideas as to why it's appending an undefined node to the document?

david_kw-I've done that trick before but it kind of defeats the purpose.


Heh - I'm lame, I just didn't have quotes around my first div creation.

Hi,

I just came across this thread. You should check out this article: http://www.alistapart.com/articles/crossbrowserscripting

It creates a fixed version of importNode() that works cross-browser.

I'm looking to do something similar, but I also need to be able to have inline javascript tags, that get executed in place, in my imported HTML - for example to serve an advertisement that is invoked with some JS code. Any ideas?

I think Prototype can do that with their Ajax.Updater() using the option evalScripts = true

http://www.prototypejs.org/api/ajax/updater

I haven't looked at the source, but I suspect they are using innerHTML though.

david_kw

I just took a look, and it looks like they are using responseText, which means I'll have little or no control over the DOM of my updated page area. Also, it seems that while they will eval() any script blocks, they aren't eval'ed in place, so, for example, a document.write, (which most advertisers still use to insert their ads), won't work correctly, and may actually overwrite the whole page.

Thanks though. Prototype has some interesting things going on. I think there's stuff to learn there.

John

document.write and ajax don't mix too well as far as I've experienced. Once the page has loaded, evoking document.write will as you said, wipe the page of all but that which is written.

I usually just keep any code that I need to execute on behalf of an xmlhttp request's response in the page from which it is called. Then I just execute that code in my call back function. If I need to add event handlers to the elements created from the xml, I simply add them from the call back function prior to appending the xhtml to the document.

Hope that helps,

Basscyst










privacy (GDPR)