cloneNode/appendChild and children

O

Olorin

Hello everyone. I'm playing around with some AJAX-ish stuff and
encountered some problem in the JS side of the universe. Maybe someone
here can suggest an alternative that works.

I have developed a simple ASP.NET application with a web page that
should display a list of users. This list page is designed to start
with an empty table (with columns defined), and, onload, send an
XmlHttp request to a server component (a.k.a. ListServer). This
ListServer is currently simulating a long-running operation. So, it
sleeps for 3 seconds, and then grabs a list of 4 users from an xml
file. It applies an xsl transformation and returns the result.

The xslt is designed to take the xml data and convert it to a
<ListChunk> root element containing a <tr> for each user in the XML
data. Each row contains 4 columns (Id, Login, Password, Name). The
javascript in the list page grabs the responseXML from the XmlHttp
object and should, for each <tr> in it, create a copy of the row
coming from the server and adding it to the list on the client.

I actually got all that to work quite fine. The js snipplet dealing
with copying the row coming from the server and adding it to the table
on the client is:

(listChunk is the responsseXML fromt he XmlHttp object, and tableBody
is the body of the table in the page)

var userRows = listChunk.getElementsByTagName("tr");
if(userRows==null || userRows.length==0)
{
EndAsyncLoading();
return;
}
for(var rowIndex=0; rowIndex<userRows.length; rowIndex++)
{
var serverRow = userRows[rowIndex];
var clientRow = tableBody.insertRow(-1);

var serverCells = serverRow.getElementsByTagName("td");
for(var cellIndex=0; cellIndex<serverCells.length; cellIndex+
+)
{
var serverCell = serverCells[cellIndex];
var clientCell = serverCell.cloneNode(true);
clientRow.appendChild(clientCell);
}
}

As you can see, it's a call to insertRow followed by a call to
cloneNode and appendChild for each cell int he row.

Today I modified the xsl on the server to make the Id and Login
columns links. The odd thing is that the table rows added to the table
on the page include the links in the first two cells, but the browser
does not let me click on them.

Even better.. if I save the resulting page locally, and then re-open
it in the same browser, the links are recognized as such and
clickable.

I'm working in Firefox 2 for now, but would certainly like to find a
solution that works at least in FF2 and IE7 (although IE7 is still
yelling at me for using appendChild /sigh)

Thanks in advance,
F.O.R.
 
R

RobG

Hello everyone. I'm playing around with some AJAX-ish stuff and
encountered some problem in the JS side of the universe. Maybe someone
here can suggest an alternative that works.

I have developed a simple ASP.NET application with a web page that
should display a list of users. This list page is designed to start
with an empty table (with columns defined), and, onload, send an
XmlHttp request to a server component (a.k.a. ListServer). This
ListServer is currently simulating a long-running operation. So, it
sleeps for 3 seconds, and then grabs a list of 4 users from an xml
file. It applies an xsl transformation and returns the result.

The xslt is designed to take the xml data and convert it to a
<ListChunk> root element containing a <tr> for each user in the XML
data.

It seems rather pointless to use XSLT to convert XML to XML. Why not
convert it to HTML and save yourself a lot of trouble?
Each row contains 4 columns (Id, Login, Password, Name). The
javascript in the list page grabs the responseXML from the XmlHttp
object and should, for each <tr> in it, create a copy of the row
coming from the server and adding it to the list on the client.

I actually got all that to work quite fine. The js snipplet dealing
with copying the row coming from the server and adding it to the table
on the client is:

(listChunk is the responsseXML fromt he XmlHttp object, and tableBody
is the body of the table in the page)

var userRows = listChunk.getElementsByTagName("tr");
if(userRows==null || userRows.length==0)

In what circumstance will userRows be null?

If supported, getElementsByTagName returns a NodeList object, always.
If it's not, attempting listChunk.getElementsByTagName("tr") will
cause your script to fail before the test is reached.

{
EndAsyncLoading();
return;
}
for(var rowIndex=0; rowIndex<userRows.length; rowIndex++)
{
var serverRow = userRows[rowIndex];
var clientRow = tableBody.insertRow(-1);

var serverCells = serverRow.getElementsByTagName("td");
for(var cellIndex=0; cellIndex<serverCells.length; cellIndex+
+)
{
var serverCell = serverCells[cellIndex];
var clientCell = serverCell.cloneNode(true);
clientRow.appendChild(clientCell);
}
}

As you can see, it's a call to insertRow followed by a call to
cloneNode and appendChild for each cell int he row.

Why not send HTML instead of XML and just append the rows to the table
(insert the HTML as the innerHTML of a div to convert it to a DOM
object, just wrap the trs in table tags)? You could also just grab
the tbody and append it to the current table or replace the current
tbody - done in one go.

Today I modified the xsl on the server to make the Id and Login
columns links. The odd thing is that the table rows added to the table
on the page include the links in the first two cells, but the browser
does not let me click on them.

Without seeing the initial XML and the resulting markup I can only
guess.
Even better.. if I save the resulting page locally, and then re-open
it in the same browser, the links are recognized as such and
clickable.

Probably because you are sending XML, not HTML. Saving it locally as
HTML then loading it back in changes how the parser interprets the
markup.

I'm working in Firefox 2 for now, but would certainly like to find a
solution that works at least in FF2 and IE7 (although IE7 is still
yelling at me for using appendChild /sigh)

Do not feed XML to IE.
 
O

Olorin

It seems rather pointless to use XSLT to convert XML to XML. Why not
convert it to HTML and save yourself a lot of trouble?

Possibly, but this is a simple mock-up, and I was following samples I
found online.
I was interested in learning a bit about the server-side application
of transforms anyway.
In what circumstance will userRows be null?

When, for instance, the server has finished sending all users.
Right now (again, this is a mock), the server will send
4 user-rows at the time, for a maximum of 4 times.
When it reaches the end of the simulkated users, it leaves
the response stream clear.
Why not send HTML instead of XML and just append the rows to the table
(insert the HTML as the innerHTML of a div to convert it to a DOM
object, just wrap the trs in table tags)? You could also just grab
the tbody and append it to the current table or replace the current
tbody - done in one go.

I actually tried to switch the xsl transformation output from xml to
html, just to see
if it made any difference, but it didn't.
I've been reading around and it seems that, indeed, cloneNode does not
copy
all attributes and/or event handlers (although in my case the href
attribute is cloned),
and that simply adding a node to an [x]html document is not enough.
See for instance this article: http://alistapart.com/articles/crossbrowserscripting
It focuses on importNode, rather than cloning and appending (like I am
doing), but the
problem seem to be the same.
Without seeing the initial XML and the resulting markup I can only
guess.

The source of users is an xml file as follows: (I'll show you a single
user, but there are 4 in the actual file)
<?xml version="1.0" encoding="UTF-8"?>
<ListPage>
<User id="1">
<Login>fred</Login>
<Password>Wilma</Password>
<FirstName>Fred</FirstName>
<LastName>Flinstone</LastName>
</User>
</ListPage>

The xslt, with the first 2 columns set to be links:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/
Transform">
<xsl:eek:utput method="xml" encoding="UTF-8" indent="yes" omit-xml-
declaration="no" standalone="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="/">
<ListChunk>
<xsl:for-each select="ListPage/User">
<tr>
<td>
<a href="http://www.google.com"><xsl:value-of
select="@id" /></a>
</td>
<td>
<a href="http://www.google.com"><xsl:value-of
select="Login" /></a>
</td>
<td>
<xsl:value-of select="Password" />
</td>
<td>
<xsl:value-of select="FirstName" />
</td>
<td>
<xsl:value-of select="LastName" />
</td>
</tr>
</xsl:for-each>
</ListChunk>
</xsl:template>
</xsl:stylesheet>

Using firebug, I've been checking what gets sent by the server to the
client, and it seems to be:
<ListChunk>
<tr>
<td><a href="http://www.google.com">1</a></td>
<td><a href="http://www.google.com">fred</a></td>
<td>Wilma</td>
<td>Fred</td>
<td>Flinstone</td>
</tr>
</ListChunk>


As I said, changing the output method of the transform from xml to
html didn't seem to make any difference.

I can share the whole thing if needed (including the javascript that
kicks off the request on page load, etc etc), but it really seem to me
that there's something going on with the cloneNode/appendChild
sequence.
In the article I mentioned above, A. Holdener explains that

Apparently, in all browsers, elements must be passed through the HTML
parser before events and style will be activated.

That might be the reason why the links are not being treated as links
(but reloading the page from a local copy makes them act like proper
links).
Or maybe not, maybe it's totally unrelated and I'm missing something
else.
Do not feed XML to IE.

....so what's the recommended cross-browser 'closest-to-standard-
compliance' approach to take for something like this ?

Thanks for reading and answering,
F.O.R.
 
R

RobG

Possibly, but this is a simple mock-up, and I was following samples I
found online.
I was interested in learning a bit about the server-side application
of transforms anyway.


When, for instance, the server has finished sending all users.

getElementsByTagName is a method of the document object. By
definition, it always returns a NodeList object. It may not have any
nodes in the list (i.e. a length of zero) but it is still a NodeList
object, not null.

Right now (again, this is a mock), the server will send
4 user-rows at the time, for a maximum of 4 times.
When it reaches the end of the simulkated users, it leaves
the response stream clear.


I actually tried to switch the xsl transformation output from xml to
html, just to see
if it made any difference, but it didn't.
From what you have posted below, your response text is not valid
HTML. TR elements must be in a table. I don't know how a browser
treats an XML "tr" node placed into an HTML document.
I've been reading around and it seems that, indeed, cloneNode does not
copy
all attributes and/or event handlers (although in my case the href
attribute is cloned),

Whether or not handlers are cloned depends on how they are added and
which browser you are using.

<URL:
http://groups.google.com.au/group/c...onclick+handler&rnum=1&hl=en#71c9b4debcac1543
cloneNode *should* copy all attributes and their values, whether or
not it does might be browser dependent:

<URL: http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-3A0ED0A4 >


[...]
The source of users is an xml file as follows: (I'll show you a single
user, but there are 4 in the actual file)
<?xml version="1.0" encoding="UTF-8"?>
<ListPage>
<User id="1">
<Login>fred</Login>
<Password>Wilma</Password>
<FirstName>Fred</FirstName>
<LastName>Flinstone</LastName>
</User>
</ListPage>

The xslt, with the first 2 columns set to be links:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/
Transform">
<xsl:eek:utput method="xml" encoding="UTF-8" indent="yes" omit-xml-
declaration="no" standalone="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="/">
<ListChunk>
<xsl:for-each select="ListPage/User">
<tr>

There you are, invalid HTML (though it might be fine as XML).

[...]
Using firebug, I've been checking what gets sent by the server to the
client, and it seems to be:
<ListChunk>
<tr>
<td><a href="http://www.google.com">1</a></td>
<td><a href="http://www.google.com">fred</a></td>
<td>Wilma</td>
<td>Fred</td>
<td>Flinstone</td>
</tr>
</ListChunk>

But that's not valid HTML.

As I said, changing the output method of the transform from xml to
html didn't seem to make any difference.

Put table tags around the tr's.

[...]
...so what's the recommended cross-browser 'closest-to-standard-
compliance' approach to take for something like this ?

Most send HTML then put it into the document using innerHTML. Look at
a couple of AJAX libraries:

<URL: http://www.ajaxtoolbox.com/ >
 
O

Olorin

Hey Rob,
thanks again for the help.

I modified my solution so that the server sends simple xml to the
browser, and the js function handling the response iterates over it
and creates the table rows and their content.

In the meanwhile, I'm still hopeful to find a solution that lets the
server sends the pre-formatted rows to the client.
As you suggested, I made the server produce html instead of xml.
It seems that, in this case, the response is received in the
responseText (rather than responseXml) of the XmlHttp object in the
browser's js.

At this point, if I was handling html content that I could happily
dump in a div or other container via .innerHtml, I guess I'd be done
(but I'd still feel uneasy about innerHtml.. I'm that kind of person).
However, as you;ve seen in the previous example, I was dealing with
table rows and such...
...is there a quick way for me to read the responseText into some tree-
like structure so that I can then iterate over it as needed (other
than parsing the html myself) ?

Thanks again,
~O
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,819
Latest member
masterdaster

Latest Threads

Top