Jump to content

appending a fragment doesn't work


nemobluesix

Recommended Posts

Hello,As you all know, IE is buggy when working with attribute innerHTML of tables. So, when getting data with ajax, instead of setting this attribute to the responseText, I'm trying to create a fragment then append it to the table as child node. In case you are wondering why I don't read the whole table, I need to set both thead and tbody from two separate ajax requests.The problem is that the fragment, even if it is correct, does not show as expected. I made a test page to reproduce the problem. Do not use Internet Explorer to test this :).

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">	<head>	<title>test</title>	<script type="text/javascript">function testf(opt){	str="<tr id=\"one\"><td>row no. 1</td></tr><tr id=\"two\"><td>row no. 2</td></tr><tr id=\"three\"><td><input type=\"text\"/></td></tr>";	var tb=document.getElementById("thebody");	if(opt)		appendCells(tb,str);	else		tb.innerHTML=str;}function appendCells(tb,resp){	var firstNodeElem=0;	xmlDoc=loadXMLString("<root>"+resp+"</root>");//like in the example, I sometimes get nodes without a root element	fragment = document.createDocumentFragment();	nodes=xmlDoc.firstChild.childNodes;//get the row nodes	for(i=0;i<nodes.length;i++){		if(!firstNodeElem && nodes[i].nodeType==1) firstNodeElem=i;//save the index of the first element node		fragment.appendChild(nodes[i].cloneNode(true));//add to the fragment	}	while(tb.hasChildNodes()) tb.removeChild(tb.firstChild);//empty the body, new nodes are comming	var str= (new XMLSerializer()).serializeToString(fragment);	tb.appendChild(fragment);	//show the content in textareas	document.getElementById("frag").value=str;	document.getElementById("innerH").value=tb.innerHTML;}function loadXMLString(txt) {	if (window.DOMParser)	{		parser=new DOMParser();		xmlDoc=parser.parseFromString(txt,"text/xml");	}	else // Internet Explorer	{		xmlDoc=new ActiveXObject("Microsoft.XMLDOM");		xmlDoc.async="false";		xmlDoc.loadXML(txt); 	}	return xmlDoc;}	</script>	<style type="text/css">		table tr td{			border: 1px solid red;		}		textarea{			width:300px;			height:300px;		}	</style></head><body>	<input type="button" value="modify by innerHTML" onclick="testf(0)"/>	<input type="button" value="modify by fragment" onclick="testf(1)"/>	<table id="thetable">		<tbody id="thebody">			<tr><td>press the buttons to modify the table</td></tr>		</tbody>	</table>	<table><tr><td>fragment content<br/><textarea id="frag"></textarea></td><td>innerHTML content<br/><textarea id="innerH"></textarea></td></tr></table></body></html>

I don't even know where to stat digging :)

Link to comment
Share on other sites

That's a pretty unconventional approach. You're correct, innerHTML is bad news for populating tables. But document fragments are a totally unnecessary middle step. The normal functions are tableObj.insertRow() and rowObj.insertCell.There is a pretty good example here. Obviously you'd need to make changes. A big change is what you've already figured out: add elements to a <tbody> element, not a <table> element itself. Fortunately, functions that work for a table also work for a tbody.

Link to comment
Share on other sites

thanks Deirdre's Dad for your reply.I choose the fragment way because in my real program I get more than simple tables. My cells can also contain form elements, anchors etc. So the insertRow and insertCell functions are insufficient and I think it's very complicated to check the tagName of every element.So far, after googling around I learned that inserting xml in the html dom will not work and I wrote a personal version of cloneNode. It might not work for everything, I didn't even test it very carefully but I'm posting it here to get some feed back :)In the test page posted earlier replace the line that reads:

fragment.appendChild(nodes[i].cloneNode(true));//add to the fragment

with

var newn=myClone(nodes[i]);if(newn) fragment.appendChild(newn);

and add a new function:

function myClone(node){		if(!node) return null;		var newn;		switch(node.nodeType){				case 1://	   ELEMENT_NODE						newn=document.createElement(node.nodeName);						break;				case 3://	   TEXT_NODE						newn=document.createTextNode(node.nodeValue);						break;				case 4://	   CDATA_SECTION_NODE						newn=document.createCDATASection(node.nodeValue);						break;				case 2://	   ATTRIBUTE_NODE				case 5://	   ENTITY_REFERENCE_NODE				case 6://	   ENTITY_NODE				case 7://	   PROCESSING_INSTRUCTION_NODE				case 8://	   COMMENT_NODE				case 9://	   DOCUMENT_NODE				case 10://	  DOCUMENT_TYPE_NODE				case 11://	  DOCUMENT_FRAGMENT_NODE				case 12://	  NOTATION_NODE						alert("untreated "+node.nodeType);						break;		}		var atts=node.attributes;		for(var i=0;atts && i<atts.length;i++){				newn.setAttribute(atts[i].nodeName,atts[i].nodeValue);		}		var c=node.firstChild;		while(c){				var newc=myClone(c);				if(newc){						newn.appendChild(newc);				}				c=c.nextSibling;		}		return newn;}

For some reason, attributes are not listed in the childNode collection so I treated them in a separate loop; maybe somebody can clear why this happens.And another issue is that the <input> element is not closed correctly (like <input/>). I'm still digging for this...

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...