Hmm seems interesting, ajaxian has an interesting story if anyone wants to read up on the debate:
“JSON vs. XML”:http://ajaxian.com/archives/json-vs-xml-the-debate
“The debate in AjaxWorld Mag”:http://ajaxian.com/archives/ajaxworld-magazine-json-versus-xml
Thanks for the post, I’ll be speding more time w/ JSON
I can see this being useful in some very specific cases, but most of the time it would seem to be easier to pull the XHTML text using XHR, and then use Regular Expressions to find the appropriate data within.
This kind of “solution” to work around the X in Ajax just helps to convince me that Json and innerHTML is the best way to go, standards or not. The relatively few problems that you run into using innerHTML are pretty minor compared to the headaches of the badly designed and inconsistently implemented XML Dom.
>>>
1. converting it to JSON on the server from wherever it came from 2. sending it to the client 3. converting it back to XHTML on the client
>>>
Yeah…
1. php_json, or any of the tools available at http://www.json.org/. Even without handy extensions, outputting Json is at least as simple as outputting XML. (In my opinion, it’s easier.)
2. XHR – Just use the responseText property
3. eval() or http://www.json.org/json.js, and you’ve got a native Javascript object to work with, instead of a klunky Dom.
It’s much faster for the browser, the code that parses it is easier to understand and simpler to read. In the real world, we spend about 80% of our time maintaining code, not writing it; simplicity and readability == $$. On top of that, Json is typically fewer bytes than XML, and with innerHTML, you can update the page with a single redraw/reflow, which saves perceived and actual time for the user.
And, Json isn’t just serialized Javascript any more. It’s extensible and simple enough that it’s fast becoming a form of serialized anything – php, java, c++, and so on.
If the best, fastest, easiest, most readable and extensible and maintainable method of asynchronous data transfer and DHTML updating is not supported by the standards, does that mean that the method is broken, or that the standards are?
Copy & paste the code below to embed this comment.
Adam Bones
The problem I see with innerHTML is that it destroys previous DOM objects.
Say you have a list. You want to apply a behaviour to each list item without using any inline JS. A response comes back with HTML for an a new <li>. Presumably you now write something like:
ul[removed] += response.responseText;
You’ve now lost all your event listeners for the other items, since those DOM objects have gone.
I have been wracking my brain for a couple of days trying to solve an issue with IE. If i use this method to import my xml response (containing html form markup), all the elements loose their names in IE.
The offending line is:
document.getElementById(”˜xhrFrame’)[removed] = document.getElementById(”˜xhrFrame’)[removed];
Before the innerHTML = innerHTML line, all the input elements have names in IE. After that line, they loose there names. I have tried retrieving the names every way I could think if and looked at those discussed in this article:
In the end, the only solution I have is to retrieve the input names before the innerHTML = innerHTML and then set them again afterwards. However, this is verbose and ugly.
Has anyone encountered this problem? Is there a better solution?
Thanks Sir; Your approach on the subject is really appreciatable Actually your presentation compels me to share a similar related theme as I am a fresher in this field earlier 2 weeks ago I send an intial js loader to the browser, namely htmlOutput
function htmlOutput(xmlUri, xslUri){
var xmlHttp = XmlHttp.create(); //XmlHttp is a cross-browser
httpRequest Class. We load the xml file
var async = true;
xmlHttp.open(“GET”, xmlUri, async);
xmlHttp.onreadystatechange = function () {
if (xmlHttp.readyState 4){ //Once loaded, the xsl stylesheet is
loaded too, then imported into the xslt processor
// TODO error control
var processor = new XSLTProcessor();
var xslHttp = XmlHttp.create();
var xslasync = true;
xslHttp.open(“GET”, xslUri, xslasync);
xslHttp.onreadystatechange = function () {
if (xslHttp.readyState 4){
processor.importStylesheet(xslHttp.responseXML);
var newDocument =
processor.transformToDocument(xmlHttp.responseXML); //xslt produce a DomDoc
var bodyChildren = new Array();
bodyChildren = newDocument.getElementsByTagName(“body”)0.childNodes;
for (var i = 0; i < bodyChildren.length; i++) { //each part of the
body is imported into the displayed doc, namely document
document.getElementsByTagName(“body”)0.appendChild(document.importNode(boÂdyChildren,
true));
}
var bodyChildren = new Array();
headChildren = newDocument.getElementsByTagName(“head”)0.childNodes;
for (var i = 0; i < headChildren.length; i++) { //same for each
part of the head is imported into the displayed doc, namely document
This is very classical. Xml is loaded, then Xsl, XsltProcessor is instanciated, filled with the stylesheet, the ransformation is made.
Finally, I import into the displayed document every node which interest me, in the body then in the head section. In that case, every node of the XHTML output produced. It works. I was happy, because everything is loaded just once, and my xmlHttp.responseXML is ready to be worked on, while the nodes are displayed and present in the DOM inspector.
But There is a problem: The script node I add via xslt does not seem to be evaluated. Its functions are not taken into consideration. Any ideas ?
Your
It’s truely right to the factor. See I have pre-existing XHTML pages I’d like to grab data from. This eliminates JSON because the data is already in XHTML format. I can import the pages as XML, but then I run into trouble with formatting markup (such as <em>). I can use innerHTML, but that means giving up DOM scripting control over the imported data.
It appears this script will let me import a chunk of XHTML from an existing page, retain existing XHTML markup such as <em> and CSS selectors, and also be able to manipulate it via the DOM.That’s how it’s work.
It seems to me that the “default styles” won’t be used if an element is not in the XHTML namespace. I saw this behavior myself when I removed the xmlns attribute from the XHTML document that I was receiving via XMLHttpRequest in one of my test scripts. (As far as I know, IE7 doesn’t support XML namespaces.)
Importing xml can have its issues, I don’t think the format will ever be 100% perfect although I haven’t tried what your doing there Matt. Wish I could help in some way :) I have spent several days do what seems like the simplest thing when working with importing xml.
If your code modified to class. Is it will work correctly, if several objects will placed on single page? For example, is XMLHttpRequest will correct transfer date?
Copy & paste the code below to embed this comment.
Michael Smith
I use serverside code as a graceful-failure option using the same XSLT as the Javascript uses. The most convenient DOM method for me is XSLTProcessor.transformToFragment(xml, targetDocument); however, I’ve had to enhance Anthony’s importNode() to import the transformed XHTML as a DocumentFragment. Here’s the additional case (looks familiar, doesn’t it?):
case document.DOCUMENT_NODE:
var newNode = document.createDocumentFragment();
if (allChildren && node.childNodes && node.childNodes.length > 0)
for (var i = 0, il = node.childNodes.length; i < il;)
newNode.appendChild(document._importNode(node.childNodes[i++], »
allChildren));
return newNode;
break;
The method uses a similar implementation of the adoptNode() function that includes tweaks for the Missing TBody Problem, the Special IE Attribute Names (className, CSSText, HTMLFor) and the Attribute Case Sensitiveness Problem.
Copy & paste the code below to embed this comment.
richardh
Firstly I buy 100% into your requirement and philosophy Anthony and I would like to thank you for taking the time to write such an amazing article.
Secondly, I would like to point the following out to the narrow minded JSON zealots in your post and also hopefully assist those who as a result are being exposed to their comments:- While JSON absolutely has it’s place, you and your programming will always benefit by broadening your outlook. Don’t assume that any one solution is best all the time every time.
There are many reasons why XHTML has a place. One reason for example is that XML is adopted right into the core of many databases and at a simplistic level this means that using XHTML internally (or a stripped down version) allows one to pass data from the core to the user with minimal/ even no transformation (and please can I ask any commenters not to labour the “gee, but that is inefficient in a database” point as the details of this aspect are well known to me) . Another is the massive supporting framework that exists for XML, a quick example being XSLT; which allows simple, elegant data transformations to XML data. Combining simplicity and elegance in my book makes for great programmatic results.
So whether you choose JSON or XHTML for your particular purpose, no matter. In my case I will for sure be taking advantage of this wonderful post.
Copy & paste the code below to embed this comment.
richardh
Just a quick update that thanks to your article, from now on it will take just 2 lines of code for me to take any subtree of a response XHTML document and append the entire subtree to any branch of the current document – and that I have tested this successfully on IE 6,7,8, FF3, current Opera, current Chrome and current Safari
Of particular benefit was
if (!document.importNode) {
parentNode[removed] = parentNode[removed];
}
for IE 6 and 7, which would have taken me an unknown amount of time to identify – as IE6 and IE7 throw no errors if this is omitted and simply displays “nothing”
And one last note is that I highly recommend that people stay away from innerHTML as quirks that I saw with it, even in the latest firefox were what started me on the dom investigation in the first place – you simply cannot be sure when innerHTML will not work…
Thanks again and good luck to all others out there grappling with cross browser compatibility – I highly recommend taking advantage of this fantastic article.
34 Reader Comments
Back to the ArticleTison Kelley
Hmm seems interesting, ajaxian has an interesting story if anyone wants to read up on the debate:
“JSON vs. XML”:http://ajaxian.com/archives/json-vs-xml-the-debate
“The debate in AjaxWorld Mag”:http://ajaxian.com/archives/ajaxworld-magazine-json-versus-xml
Thanks for the post, I’ll be speding more time w/ JSON
Michael Newton
… “E4X”:http://en.wikipedia.org/wiki/E4X to be widely supported, then we’re in business!
Michael Newton
The preview showed the proper characters, not the entities!
Stewart Ulm
I can see this being useful in some very specific cases, but most of the time it would seem to be easier to pull the XHTML text using XHR, and then use Regular Expressions to find the appropriate data within.
Jeffrey Zeldman
Yup, sorry. Should be fixed in the New CMS™ (coming soon).
Jeffrey Zeldman
(The preview showed the ™ symbol, but the system printed the entity. Whee.)
Isaac Schlueter
XML is not worth the trouble.
This kind of “solution” to work around the X in Ajax just helps to convince me that Json and innerHTML is the best way to go, standards or not. The relatively few problems that you run into using innerHTML are pretty minor compared to the headaches of the badly designed and inconsistently implemented XML Dom.
>>>
1. converting it to JSON on the server from wherever it came from 2. sending it to the client 3. converting it back to XHTML on the client
>>>
Yeah…
1. php_json, or any of the tools available at http://www.json.org/. Even without handy extensions, outputting Json is at least as simple as outputting XML. (In my opinion, it’s easier.)
2. XHR – Just use the responseText property
3. eval() or http://www.json.org/json.js, and you’ve got a native Javascript object to work with, instead of a klunky Dom.
It’s much faster for the browser, the code that parses it is easier to understand and simpler to read. In the real world, we spend about 80% of our time maintaining code, not writing it; simplicity and readability == $$. On top of that, Json is typically fewer bytes than XML, and with innerHTML, you can update the page with a single redraw/reflow, which saves perceived and actual time for the user.
And, Json isn’t just serialized Javascript any more. It’s extensible and simple enough that it’s fast becoming a form of serialized anything – php, java, c++, and so on.
If the best, fastest, easiest, most readable and extensible and maintainable method of asynchronous data transfer and DHTML updating is not supported by the standards, does that mean that the method is broken, or that the standards are?
Thankfully, standards change, albeit painfully slowly.
http://www.google.com/search?q=jsonrequest
It’s a nicely written article, though. Thankfully we all get to learn from your headache instead of having our own :)
Anthony Holdener
I’m just curious if any Safari users out there have tried this solution and found that it worked or didn’t work?
Jakub RoztoÄ?il
“Internet Explorer did not define document.ELEMENT_NODE and the other node types as part of its DOM implementation.”
These constants are defined in Node interface (Node.ELEMENT_NODE, …):
http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-1950641247
AnyNode.*_NODE works AFAIK only in Firefox.
Adam Bones
The problem I see with innerHTML is that it destroys previous DOM objects.
Say you have a list. You want to apply a behaviour to each list item without using any inline JS. A response comes back with HTML for an a new <li>. Presumably you now write something like:
ul[removed] += response.responseText;
You’ve now lost all your event listeners for the other items, since those DOM objects have gone.
matt snider
I have been wracking my brain for a couple of days trying to solve an issue with IE. If i use this method to import my xml response (containing html form markup), all the elements loose their names in IE.
The offending line is:
document.getElementById(”˜xhrFrame’)[removed] = document.getElementById(”˜xhrFrame’)[removed];
An example XML:
<response type=“object” id=“RequestFIDetail”>
<h1>American Express Cards</h1>
<div class=“content”>
<form class=‘yodleeForm’ acti>
<input type=“hidden” name=“fiId” value=“12”/>
<input type=“hidden” name=“filoginId” value=“0”/>
<input type=“hidden” name=“refresh” value=“true”/>
<div class=“row”>
<span class=“label”>User ID</span>
<span class=“ff”><input class=“ssl_search” type=“text” name=“LOGIN” value=”” size=“20” maxlength=“40” /></span>
</div>
<div class=“row”>
<span class=“label”>Password</span>
<span class=“ff”><input class=“ssl_search” type=“password” name=“PASSWORD” value=”” size=“20” maxlength=“40” /></span>
</div>
<div class=“row”>
<span class=“ff”><input type=“submit” name=“submit” value=“Login” class=“submit” /></span>
</div>
</form>
</div>
</response>
Before the innerHTML = innerHTML line, all the input elements have names in IE. After that line, they loose there names. I have tried retrieving the names every way I could think if and looked at those discussed in this article:
http://tobielangel.com/2007/1/11/attribute-nightmare-in-ie
In the end, the only solution I have is to retrieve the input names before the innerHTML = innerHTML and then set them again afterwards. However, this is verbose and ugly.
Has anyone encountered this problem? Is there a better solution?
thanks
-matt
Richard Walter
Maybe someone could convince “Dean Edwards”:http://dean.edwards.name to add this to his base2 project?????
website design
Thanks Sir; Your approach on the subject is really appreciatable Actually your presentation compels me to share a similar related theme as I am a fresher in this field earlier 2 weeks ago I send an intial js loader to the browser, namely htmlOutput
function htmlOutput(xmlUri, xslUri){
var xmlHttp = XmlHttp.create(); //XmlHttp is a cross-browser
httpRequest Class. We load the xml file
var async = true;
xmlHttp.open(“GET”, xmlUri, async);
xmlHttp.onreadystatechange = function () {
if (xmlHttp.readyState 4){ //Once loaded, the xsl stylesheet is
loaded too, then imported into the xslt processor
// TODO error control
var processor = new XSLTProcessor();
var xslHttp = XmlHttp.create();
var xslasync = true;
xslHttp.open(“GET”, xslUri, xslasync);
xslHttp.onreadystatechange = function () {
if (xslHttp.readyState 4){
processor.importStylesheet(xslHttp.responseXML);
var newDocument =
processor.transformToDocument(xmlHttp.responseXML); //xslt produce a DomDoc
var bodyChildren = new Array();
bodyChildren = newDocument.getElementsByTagName(“body”)0.childNodes;
for (var i = 0; i < bodyChildren.length; i++) { //each part of the
body is imported into the displayed doc, namely document
document.getElementsByTagName(“body”)0.appendChild(document.importNode(boÂdyChildren,
true));
}
var bodyChildren = new Array();
headChildren = newDocument.getElementsByTagName(“head”)0.childNodes;
for (var i = 0; i < headChildren.length; i++) { //same for each
part of the head is imported into the displayed doc, namely document
document.getElementsByTagName(“head”)0.appendChild(document.importNode(heÂadChildren,
true));
}
}
}
xslHttp.send(null);
}
}
xmlHttp.send(null);
}
This is very classical. Xml is loaded, then Xsl, XsltProcessor is instanciated, filled with the stylesheet, the ransformation is made.
Finally, I import into the displayed document every node which interest me, in the body then in the head section. In that case, every node of the XHTML output produced. It works. I was happy, because everything is loaded just once, and my xmlHttp.responseXML is ready to be worked on, while the nodes are displayed and present in the DOM inspector.
But There is a problem: The script node I add via xslt does not seem to be evaluated. Its functions are not taken into consideration. Any ideas ?
Your
Danger Mouse
What is the license of this code? I’m assuming some kind of GPL…?
design development web
It’s truely right to the factor. See I have pre-existing XHTML pages I’d like to grab data from. This eliminates JSON because the data is already in XHTML format. I can import the pages as XML, but then I run into trouble with formatting markup (such as <em>). I can use innerHTML, but that means giving up DOM scripting control over the imported data.
It appears this script will let me import a chunk of XHTML from an existing page, retain existing XHTML markup such as <em> and CSS selectors, and also be able to manipulate it via the DOM.That’s how it’s work.
Anthony Holdener
As per “ALA’s Copyright”:http://www.alistapart.com/copyright/ all of the source code is free to use—no questions asked.
Anthony Holdener
As per “ALA’s Copyright”:http://www.alistapart.com/copyright/ all of the source code is free to use—no questions asked.
David Shuford
It seems to me that the “default styles” won’t be used if an element is not in the XHTML namespace. I saw this behavior myself when I removed the xmlns attribute from the XHTML document that I was receiving via XMLHttpRequest in one of my test scripts. (As far as I know, IE7 doesn’t support XML namespaces.)
Try this:
image_description:
<div id=“imageDescription” >
Paragraph 1
Paragraph 2
…
Paragraph n
</div>
Jason Congo
Importing xml can have its issues, I don’t think the format will ever be 100% perfect although I haven’t tried what your doing there Matt. Wish I could help in some way :) I have spent several days do what seems like the simplest thing when working with importing xml.
Alexey Burlaka
Great work.
But i have a question.
If your code modified to class. Is it will work correctly, if several objects will placed on single page? For example, is XMLHttpRequest will correct transfer date?
Thank’s, AlexeyGfi
Michael Smith
I use serverside code as a graceful-failure option using the same XSLT as the Javascript uses. The most convenient DOM method for me is XSLTProcessor.transformToFragment(xml, targetDocument); however, I’ve had to enhance Anthony’s importNode() to import the transformed XHTML as a DocumentFragment. Here’s the additional case (looks familiar, doesn’t it?):
case document.DOCUMENT_NODE:
var newNode = document.createDocumentFragment();
if (allChildren && node.childNodes && node.childNodes.length > 0)
for (var i = 0, il = node.childNodes.length; i < il;)
newNode.appendChild(document._importNode(node.childNodes[i++], »
allChildren));
return newNode;
break;
ccblogger
Thanks for the article and comments, I found it very useful.
Check out the following post to see how to create pure HTML templates that are compatible with IE:
http://ccsoftwarefactory.com/blog/index.php/2009/10/26/html-templates-with-javascript-and-external-xml
The method uses a similar implementation of the adoptNode() function that includes tweaks for the Missing TBody Problem, the Special IE Attribute Names (className, CSSText, HTMLFor) and the Attribute Case Sensitiveness Problem.
Comments welcome.
Cheers!
richardh
Firstly I buy 100% into your requirement and philosophy Anthony and I would like to thank you for taking the time to write such an amazing article.
Secondly, I would like to point the following out to the narrow minded JSON zealots in your post and also hopefully assist those who as a result are being exposed to their comments:- While JSON absolutely has it’s place, you and your programming will always benefit by broadening your outlook. Don’t assume that any one solution is best all the time every time.
There are many reasons why XHTML has a place. One reason for example is that XML is adopted right into the core of many databases and at a simplistic level this means that using XHTML internally (or a stripped down version) allows one to pass data from the core to the user with minimal/ even no transformation (and please can I ask any commenters not to labour the “gee, but that is inefficient in a database” point as the details of this aspect are well known to me) . Another is the massive supporting framework that exists for XML, a quick example being XSLT; which allows simple, elegant data transformations to XML data. Combining simplicity and elegance in my book makes for great programmatic results.
So whether you choose JSON or XHTML for your particular purpose, no matter. In my case I will for sure be taking advantage of this wonderful post.
Thank you again
richardh
Just a quick update that thanks to your article, from now on it will take just 2 lines of code for me to take any subtree of a response XHTML document and append the entire subtree to any branch of the current document – and that I have tested this successfully on IE 6,7,8, FF3, current Opera, current Chrome and current Safari
Of particular benefit was
if (!document.importNode) {
parentNode[removed] = parentNode[removed];
}
for IE 6 and 7, which would have taken me an unknown amount of time to identify – as IE6 and IE7 throw no errors if this is omitted and simply displays “nothing”
And one last note is that I highly recommend that people stay away from innerHTML as quirks that I saw with it, even in the latest firefox were what started me on the dom investigation in the first place – you simply cannot be sure when innerHTML will not work…
Thanks again and good luck to all others out there grappling with cross browser compatibility – I highly recommend taking advantage of this fantastic article.