1

I'm doing an Ajax request in which I have the server returning well-formed HTML, specifically table row ("tr") elements. I had hoped to use the DOM to insert these rows into a table by reading from the "responseXML" member on the request object (XMLHttpRequest). Basically, I have

var myTable = document.getElementById("myTable");
var tableRows = responseXML.getElementsByTagName("tr");
for (var i = 0; i < tableRows.length; i++) {
  myTable.appendChild(tableRows[i]);
}

Lots of things go right here. The "responseXML" contains the correct data, and the call to "getElementsByTagName" also works correctly. Sadly, it breaks down when I try to add a row by calling appendChild (I get a vague javascript error). I think this is because although each item in the "tableRows" array is a correct XML element, the browser can't automatically interpret it as an HTML element. I've noticed that if I make a new using document.createElement("tr") and examine it during run-time, it looks different in memory from the items in the "tableRows" array. Also, I am able insert that new (the one made with "createElement") into the table without an error.

Is there a good way to do what I'm trying to do here? I was hoping that I could make the browser interpret the elements correctly by returning the content as XML and using responseXML/getElementsByTagName. That way, I could avoid using responseText for what is really HTML, and I could also avoid using "innerHTML" because it's not a standard. Most examples online use innerHTML, however. Is that really the best way?

nttaylor
  • 788
  • 3
  • 11
  • 25
  • You might tell us what your "vague javascript error" says -- some form of WRONG_DOCUMENT_ERR? It's probably because, as @Vivin hinted, you cannot append `tableRows[i]` to your page because appendChild requires its argument to be in the same document as the parent element, whereas responseXML is a separate document. See http://www.w3.org/DOM/faq.html#ownerdoc – LarsH Apr 15 '11 at 21:51
  • The javascript error is "htmlfile: no such interface supported". Do you think this is the case you are describing? It still seems weird that I can successfully run "getElementsByTagName" on the responseXML, get a correct list of TR elements, and yet I can't append those TR elements the way I can with a freshly minted document.createElement("TR"). – nttaylor Apr 18 '11 at 15:20
  • Btw, I also tried it by creating an instance of Microsoft's DOMParser (xmlDoc = new ActiveXObject("Microsoft.XMLDOM")), but that produced a different vague run-time error, namely: "Microsoft JScript runtime error: Type mismatch". Should there be a difference bewteen running "getElementsByTagName" directly on the XML string and running it on the "XMLDoc" object after executing the line "xmlDoc.loadXML(responseXML);" ? In both instances, "getElementsByTagName" successfully yields a list of TR elements, but in neither case can I "appendChild" with one of these elements as an argument. – nttaylor Apr 18 '11 at 15:25
  • @Noel, yes, this sounds like the same error, as IE expresses it. When you use `document.createElement()`, the owner document of the resulting node is `document`. Therefore there is no problem appending that node into `document`. But `responseXML` is a separate document, and its descendants' owner document is `responseXML`, not `document`. So you can't append those nodes without importing. Like grafting apples onto an orange tree. This prohibition is in accordance with the DOM standard (see W3C link in prev.comment), although some browsers are lax about it. – LarsH Apr 18 '11 at 15:31
  • Thank you for your great help and explanataions LarsH. Looks like I'm stuck using .innerHTML :) On the other hand, that seems to be the most common solution to this kind of problem, so maybe it's all right. – nttaylor Apr 18 '11 at 15:36
  • 1
    Actually, I think I've avoided it by implementing my own version of "importNode", which was made possible by reading the excellent essay referenced by Vivin Paliath below! Highly recommended. – nttaylor Apr 18 '11 at 17:03
  • Good! I started reading that essay, hoping it would give a better solution, but it seemed complicated. Glad you got it to work. – LarsH Apr 19 '11 at 03:36

2 Answers2

2

If it is well-formed HTML, you can try using document.importNode() like so:

//myXHTML is your HTML/XHTML (if it is well-formed, you should be ok).
//myObject is the parent node. This is the node you want to insert your HTML into
function insertXHTML(myXHTML, myObject) {
   var parser = new DOMParser();

   //Doing this to make sure that there is just one root node.
   //You can change the namespace, or not use it at all.
   var xmlToParse = "<div xmlns = \"http://www.w3.org/1999/xhtml\">" + myXHTML + "</div>";
   var XMLdoc = parser.parseFromString(xmlToParse, "application/xhtml+xml");

   var root = XMLdoc.documentElement;

   for(i = 0; i < root.childNodes.length; i++) {
       myObject.appendChild(document.importNode(root.childNodes[i], true));
   }
}

This is code I wrote some time ago (to get around the use of innerHTML); it should still work. The only thing I am worried about is DOMParser(); not sure how cross-browser it is.

EDIT

The problem with your approach (doing things with just pure JS) is that you'll run into cross-browser issues :(. For importNode, take a look at this cross-browser implementation. Your other option is to use something like jQuery. It normalizes a lot of the cross-browser differences.

Vivin Paliath
  • 94,126
  • 40
  • 223
  • 295
0

If the only content in the XMLHTTPResponse is the content for the table element on the page then yeah, use the document.getElementById('myTable').innerHTML = responseXML;

I would say that is the standard for updating HTML asynchronously.

jonsinfinity
  • 187
  • 3
  • 16