Dynamically Generated table does not apply styles

N

Nikron

Hi,

I have dynamically generated a table in javascript on a button click
event. Each cell in the table has a style as well as a onmouseover and
onmouseout event that changes the style of that cell. My problem is it
works perfectly in FF but not in IE7. Below is the code for this,
what's wierd is that when I view the individual TD tags within IE7's
developer toolbar all the style and class attributes are there, they
just aren't shown on the screen.

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Untitled Page</title>
<style type="text/css">
.tableGRID
{
font-family:verdana;
font-size:10px;
width:100%;
border:solid 1px lightgray;
border-collapse: collapse;
}
.tbodyGRID
{
font-family:verdana;
font-size:10px;
}
.tdGRID
{
font-family:verdana;
font-size:10px;
border-right:solid 1px lightgray;
border-bottom:solid 1px lightgray;
background-color:white;
}
.tdGRIDMouseOver
{
font-family:verdana;
font-size:10px;
border-right:solid 1px lightgray;
border-bottom:solid 1px lightgray;
background-color:yellow;
cursor:hand;
}
</style>

<script language="javascript" type="text/javascript">

function BuildGrid(TargetDIV)
{
if (document.getElementById(TargetDIV))
{
var divTarget = document.getElementById(TargetDIV);

var tblGrid = document.createElement("table");
var tblBody = document.createElement("tbody");

// add the body to the grid
tblGrid.appendChild(tblBody);

// Set the style for the table
tblGrid.setAttribute("class","tableGRID")

for (var rowCount=0;rowCount < 10; rowCount++)
{
// Create a tr element
var trGrid = tblGrid.insertRow(-1); // the -1
denotes that it must add it to the end

for (var colCount=0;colCount < 20; colCount++)
{
// Create a td element
var tdGrid = trGrid.insertCell(-1); // the -1
denotes that it must add it to the end

// Set the attributes

tdGrid.setAttribute("onmouseover","javascript:this.className='tdGRIDMouseOver';");

tdGrid.setAttribute("onmouseout","javascript:this.className='tdGRID';");
tdGrid.setAttribute("class","tdGRID");

// Create the actual value of the td element
var cellValue =
document.createTextNode(rowCount + ":" + colCount);//rowCount + ":" +
colCount

// Add the actual value to the td element
tdGrid.appendChild(cellValue);

// Add the td element to the tr element
trGrid.appendChild(tdGrid);
}

// Add the tr element to the table body element
(Otherwise it won't work in IE)
tblBody.appendChild(trGrid)
}

divTarget.appendChild(tblGrid);
}
else
{
alert("The layer that the grid must be built is cannot
be found.");
}
}
</script>

</head>
<body>
<form id="form1" runat="server">
<a href="#" onclick="javascript:BuildGrid('divMyGrid');">click
here</a>
<div id="divMyGrid">
</div>
</form>
</body>
</html>
 
D

David Mark

Hi,

I have dynamically generated a table in javascript on a button click
event. Each cell in the table has a style as well as a onmouseover and
onmouseout event that changes the style of that cell. My problem is it
works perfectly in FF but not in IE7. Below is the code for this,
what's wierd is that when I view the individual TD tags within IE7's
developer toolbar all the style and class attributes are there, they
just aren't shown on the screen.

<html xmlns="http://www.w3.org/1999/xhtml">

Is it valid XHTML?
<head runat="server">

That answers that question. It isn't valid anything.
<title>Untitled Page</title>
<style type="text/css">
.tableGRID
{
font-family:verdana;
font-size:10px;

This is a bad idea. Verdana is a relatively large font, so you styled
it really small to compensate. If another sans-serif font is
substituted by the user agent, it will be microscopic. Futhermore,
never use pixel units for font sizes. Most IE users don't know how to
scale them.
width:100%;
border:solid 1px lightgray;
border-collapse: collapse;
}
.tbodyGRID
{
font-family:verdana;
font-size:10px;

The "C" in CSS stands for cascading.
}
.tdGRID
{
font-family:verdana;
font-size:10px;
border-right:solid 1px lightgray;
border-bottom:solid 1px lightgray;
background-color:white;
}
.tdGRIDMouseOver
{
font-family:verdana;
font-size:10px;
border-right:solid 1px lightgray;
border-bottom:solid 1px lightgray;
background-color:yellow;
cursor:hand;
}
</style>

<script language="javascript" type="text/javascript">

Get rid of that language attribute.
function BuildGrid(TargetDIV)
{
if (document.getElementById(TargetDIV))
{
var divTarget = document.getElementById(TargetDIV);

var tblGrid = document.createElement("table");
var tblBody = document.createElement("tbody");

// add the body to the grid
tblGrid.appendChild(tblBody);

Append the table to its container before appending children.
// Set the style for the table
tblGrid.setAttribute("class","tableGRID")

Use tblGrid.className = "tableGrid" and avoid setAttribute as IE's
implementation is buggy and non-standard.
for (var rowCount=0;rowCount < 10; rowCount++)
{
// Create a tr element
var trGrid = tblGrid.insertRow(-1); // the -1
denotes that it must add it to the end

for (var colCount=0;colCount < 20; colCount++)
{
// Create a td element
var tdGrid = trGrid.insertCell(-1); // the -1
denotes that it must add it to the end

// Set the attributes

tdGrid.setAttribute("onmouseover","javascript:this.className='tdGRIDMouseOv­er';");

Don't ever set event handlers like this. See the className example
above. And global replace "javascript:" to "" in this page.
tdGrid.setAttribute("onmouseout","javascript:this.className='tdGRID';");
tdGrid.setAttribute("class","tdGRID");

// Create the actual value of the td element
var cellValue =
document.createTextNode(rowCount + ":" + colCount);//rowCount + ":" +
colCount

// Add the actual value to the td element
tdGrid.appendChild(cellValue);

// Add the td element to the tr element
trGrid.appendChild(tdGrid);
}

// Add the tr element to the table body element
(Otherwise it won't work in IE)

You are supposed to append rows to the table body. I believe Firefox
saves you a step by creating one automatically.
tblBody.appendChild(trGrid)
}

divTarget.appendChild(tblGrid);
}
else
{
alert("The layer that the grid must be built is cannot
be found.");

All your base is now belong to us.

You just created a lot of memory leaks. Set all of the variables that
contain element references to null.
</script>

</head>
<body>
<form id="form1" runat="server">
<a href="#" onclick="javascript:BuildGrid('divMyGrid');">click
here</a>
<div id="divMyGrid">
</div>
</form>
</body>
</html>

Best of luck.
 
T

Thomas 'PointedEars' Lahn

Nikron said:
I have dynamically generated a table in javascript on a button click
event. Each cell in the table has a style as well as a onmouseover and
onmouseout event that changes the style of that cell. My problem is it
works perfectly in FF but not in IE7. Below is the code for this,
what's wierd is that when I view the individual TD tags within IE7's
developer toolbar all the style and class attributes are there, they
just aren't shown on the screen.

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
^^^^^^
Whatever the rest of your source, it is usually irrelevant when it comes to
client-side (DOM) scripting. The client-side code is relevant then.

Use em or % instead. px cannot be scaled in IE 6.
[...]
.tdGRIDMouseOver
{
[...]
background-color:yellow;
http://www.w3.org/QA/Tips/color
cursor:hand;

http://jigsaw.w3.org/css-validator/

}
</style>

<script language="javascript" type="text/javascript">

The `language' attribute is deprecated long since (invalid in HTML 4.01
Strict, XHTML 1.0 Strict and XHTML 1.1) and unnecessary.

http://validator.w3.org/
function BuildGrid(TargetDIV)
{
if (document.getElementById(TargetDIV))
{
var divTarget = document.getElementById(TargetDIV);

var tblGrid = document.createElement("table");
var tblBody = document.createElement("tbody");
// add the body to the grid
tblGrid.appendChild(tblBody);

Implementing some feature tests would be wise.

http://jibbering.com/faq/faq_notes/not_browser_detect.html#bdFD
// Set the style for the table
tblGrid.setAttribute("class","tableGRID")

Don't use the badly implemented setAttribute(), where a property access
suffices:

tblGrid.className = "tableGrid";

http://wwww.w3.org/TR/DOM-Level-2-HTML/html.html
[building a table with DOM Level 2]
http://www.quirksmode.org/dom/innerhtml.html

<body>
<form id="form1" runat="server">

See above.
<a href="#" onclick="javascript:BuildGrid('divMyGrid');">click
here</a>

What about users without client-side DOM scripting support?


PointedEars
 
R

RobG

Append the table to its container before appending children.

Why? A table with no tbody or rows is invalid, from that standpoint I
think it's better to add it after having added all it's descendants.
Do you know of any cases where they *must* be added afterward?

Use tblGrid.className = "tableGrid" and avoid setAttribute as IE's
implementation is buggy and non-standard.

If you are using insertRow, you can dispense with adding a tbody
manually. insertRow works on any HTMLTable or HTMLTableSection
element.

<URL: http://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-39872903 >


[...]
Since insertCell has been used, the td has already been added to the
row. This line effectively does nothing.
You are supposed to append rows to the table body. I believe Firefox
saves you a step by creating one automatically.

Since insertRow was used, there is no need to bother with a tbody
element at all, and the row is already added to the table. If you
were going to do it manually, you should append the row to the tbody,
not the table, but as I said above, there's no need for appendChild at
all.
All your base is now belong to us.


You just created a lot of memory leaks. Set all of the variables that
contain element references to null.

How? IE's memory leaks require closures and circular references, I
didn't see any.
 
N

Nikron

Thanks for the reply, it has cleared up a lot of things. The
onmouseover and onmouseout events still dont work. I have tried them
the way you mentioned as in tdGrid.onmouseover =
"this.className='tdGrid'"; as well as the setAttribute way but its not
working. Any ideas why?
 
D

David Mark

Thanks for the reply, it has cleared up a lot of things. The
onmouseover and onmouseout events still dont work. I have tried them
the way you mentioned as in tdGrid.onmouseover =
"this.className='tdGrid'"; as well as the setAttribute way but

That's not exactly what I mentioned.

its not
working. Any ideas why?

tdGrid.onmouseover = function() { this.className='tdGrid' };
 
D

David Mark

Why? A table with no tbody or rows is invalid, from that standpoint I
think it's better to add it after having added all it's descendants.

See the link at the bottom about memory leaks in IE.
Do you know of any cases where they *must* be added afterward?
Use tblGrid.className = "tableGrid" and avoid setAttribute as IE's
implementation is buggy and non-standard.

If you are using insertRow, you can dispense with adding a tbody
manually. insertRow works on any HTMLTable or HTMLTableSection
element.

<URL:http://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-39872903>

[...]

Since insertCell has been used, the td has already been added to the
row. This line effectively does nothing.
You are supposed to append rows to the table body. I believe Firefox
saves you a step by creating one automatically.

Since insertRow was used, there is no need to bother with a tbody
element at all, and the row is already added to the table. If you
were going to do it manually, you should append the row to the tbody,
not the table, but as I said above, there's no need for appendChild at
all.






All your base is now belong to us.

You just created a lot of memory leaks. Set all of the variables that
contain element references to null.

How? IE's memory leaks require closures and circular references, I
didn't see any.

That is one of several ways to leak memory in IE.

http://msdn2.microsoft.com/en-us/library/Bb250448.aspx
 
N

Nikron

Thanks for the reply, it has cleared up a lot of things. The
onmouseover and onmouseout events still dont work. I have tried them
the way you mentioned as in tdGrid.onmouseover =
"this.className='tdGrid'"; as well as the setAttribute way but its not
working. Any ideas why?

Hi,

I've managed to get the events working as follows:

tdGrid.onmouseover = function(){this.className='tdGRIDMouseOver';}

Hope this helps anyone else who has this problem.

Thanks for the help
 
R

RobG

That is one of several ways to leak memory in IE.

http://msdn2.microsoft.com/en-us/library/Bb250448.aspx

That article lists 4 patterns, one of which isn't actually a leak.
The OP's code doesn't seem to have either circular references or
circular references with closures, so I guess you're referring to the
bit about "Cross-Page Leaks" combined with the "DOM Insertion Order
Leak", where it states "This method can cause leaks through temporary
objects if other conditions are met."

The "other condition" appears to be attaching scripts at the same time
using something like:

createElement("<div onClick='foo()'>")


I didn't see that in the OP's code, or maybe I've misunderstood the
article.
 
D

David Mark

That article lists 4 patterns, one of which isn't actually a leak.
The OP's code doesn't seem to have either circular references or
circular references with closures, so I guess you're referring to the
bit about "Cross-Page Leaks" combined with the "DOM Insertion Order
Leak", where it states "This method can cause leaks through temporary
objects if other conditions are met."

The "other condition" appears to be attaching scripts at the same time
using something like:

createElement("<div onClick='foo()'>")

That's an example they used. It is unclear as to whether this is the
only way to create cross-page leaks.

They go on to talk about "wiring up" events after all elements have
been attached to the document, which indicates that there is an issue
with attaching handlers before DOM insertion.

All I can take from this is that building a tree of elements and
inserting them all at once has pitfalls and this is especially true
when event handlers are involved (inline or not.)

Additionally, in the examples they set every variable that is used to
create an element to null when finished (whether it has events
attached or not.) The OP did not do this and that is why I mentioned
that there was a potential for leaks.
 
R

RobG

That's an example they used. It is unclear as to whether this is the
only way to create cross-page leaks.

It is unclear to me whether the specific example is required, or
whether adding them in general is an issue. It is known that using
function statements like:

tdGrid.onmouseover = function(){...};


will create a leak, but references are safe:

function doClick(){...}

function addClick(){
...
tdGrid.onmouseover = doClick;
...
}

They go on to talk about "wiring up" events after all elements have
been attached to the document, which indicates that there is an issue
with attaching handlers before DOM insertion.

It makes life difficult though - if you add say 20 elements that you
want handlers on, it suggests adding them, then going back and getting
references to them again to add the handlers. I don't think it avoids
the issue anyway - there are many threads in the archives about this
stuff, I haven't seen any that discuss that particular method as a way
to avoid leaks.

That is not to say it doesn't work, just that it doesn't seem to have
been mentioned before.

All I can take from this is that building a tree of elements and
inserting them all at once has pitfalls and this is especially true
when event handlers are involved (inline or not.)

Additionally, in the examples they set every variable that is used to
create an element to null when finished (whether it has events
attached or not.) The OP did not do this and that is why I mentioned
that there was a potential for leaks.

The OP's final solution was to use a function statement, unfortunately
no one offered a leak-proof alternative.
 
D

David Mark

[...]
You just created a lot of memory leaks. Set all of the variables that
contain element references to null.
How? IE's memory leaks require closures and circular references, I
didn't see any.
That is one of several ways to leak memory in IE.
http://msdn2.microsoft.com/en-us/library/Bb250448.aspx
That article lists 4 patterns, one of which isn't actually a leak.
The OP's code doesn't seem to have either circular references or
circular references with closures, so I guess you're referring to the
bit about "Cross-Page Leaks" combined with the "DOM Insertion Order
Leak", where it states "This method can cause leaks through temporary
objects if other conditions are met."
The "other condition" appears to be attaching scripts at the same time
using something like:
createElement("<div onClick='foo()'>")
That's an example they used. It is unclear as to whether this is the
only way to create cross-page leaks.

It is unclear to me whether the specific example is required, or
whether adding them in general is an issue. It is known that using
function statements like:

tdGrid.onmouseover = function(){...};

will create a leak, but references are safe:

function doClick(){...}

function addClick(){
...
tdGrid.onmouseover = doClick;
...
}
They go on to talk about "wiring up" events after all elements have
been attached to the document, which indicates that there is an issue
with attaching handlers before DOM insertion.

It makes life difficult though - if you add say 20 elements that you
want handlers on, it suggests adding them, then going back and getting
references to them again to add the handlers. I don't think it avoids

I guess you mean if you aren't using seperate variables for each of
the 20 elements.
the issue anyway - there are many threads in the archives about this
stuff, I haven't seen any that discuss that particular method as a way
to avoid leaks.

I have no idea if MS knows what they are talking about or not (but it
is their browser.) Whether somebody else mentioned the technique
doesn't give me any further insight.
That is not to say it doesn't work, just that it doesn't seem to have
been mentioned before.



The OP's final solution was to use a function statement, unfortunately
no one offered a leak-proof alternative.

No. I didn't catch that specific problem. Setting the variables to
null won't help that? If not, I guess the only way to avoid that type
of leak is to reference an existing function. (?)
 
R

RobG

I guess you mean if you aren't using seperate variables for each of
the 20 elements.

Heaven forbid! :)

Maybe an array to hold the references...

I have no idea if MS knows what they are talking about or not (but it
is their browser.) Whether somebody else mentioned the technique
doesn't give me any further insight.

I guess someone should test it...

No. I didn't catch that specific problem. Setting the variables to
null won't help that?

The closure involves the activation object of the outer function
itself, only way to break it is to remove the handler, say by setting
it to null when the unload event occurs:

If not, I guess the only way to avoid that type
of leak is to reference an existing function. (?)

Avoidance is always best, you can use references or if you want to
pass parameters, use the Function constructor:

var foo = 'bar';
tdGrid.onmouseover = Function('alert("foo is ' + foo + '")');

which is not favoured by some. An alternative is cleaning up
afterward, though it requires a bit more effort to design properly
(frameworks for adding, remembering and removing handlers are
reasonably common, there are a few in the archives here and in many
script libraries).
 
D

David Mark

[snip]
No. I didn't catch that specific problem. Setting the variables to
null won't help that?

The closure involves the activation object of the outer function
itself, only way to break it is to remove the handler, say by setting
it to null when the unload event occurs:

I do that when I am aware of creating circular references. In this
case, I see the circular reference, but can't understand why setting
the variable referencing the created element to null wouldn't break
the cycle.
Avoidance is always best, you can use references or if you want to
pass parameters, use the Function constructor:

var foo = 'bar';
tdGrid.onmouseover = Function('alert("foo is ' + foo + '")');

which is not favoured by some. An alternative is cleaning up

I know JSLint doesn't like it. But then it doesn't like
document.write for the same reason (it claims they "might" be eval's.)
afterward, though it requires a bit more effort to design properly
(frameworks for adding, remembering and removing handlers are
reasonably common, there are a few in the archives here and in many
script libraries).

I have my own, but only push methods onto the cleanup stack if I know
they create circular references (eg associating a DOM element's events
with methods of an object that holds a reference to the element.)
 
R

RobG

On Aug 9, 10:37 am, David Mark <[email protected]> wrote:
[snip]
No. I didn't catch that specific problem. Setting the variables to
null won't help that?
The closure involves the activation object of the outer function
itself, only way to break it is to remove the handler, say by setting
it to null when the unload event occurs:

I do that when I am aware of creating circular references. In this
case, I see the circular reference, but can't understand why setting
the variable referencing the created element to null wouldn't break
the cycle.

Given:

function addClick(id){
var domNode = document.getElementById(id);
domNode.onclick = function(){...};

// Break circular reference
domNode = null;
}

Without that last statement, we have Richard's example:

"DOM_Node.onevent -> function_object.[[scope]] ->
scope_chain -> Activation_object.nodeRef -> DOM_Node"

Setting domNode to null inside the function removes the circular
reference, the chain is broken and as far as I can tell, you are
right: there is no leak (I've done a few tests that seem to confirm
it).

That seems to agree with Douglas Crockford's article[1] on memory
leaks where he points out that circular references are the issue, not
closures. I also went back and did a search on Richard Cornford's
posts to clj and he says the same thing (repeatedly, actually :-x) - I
guess I was blind-sided by the association with closures.

Re-reading Richard's article I can see now what he was getting at:
when using closures and event handlers, it is common to create
circular references and leave them hanging around. Perhaps in the
conclusion about memory leaks he could include a something about
removing them by setting references to null where possible in addition
to the current advice about removing the handlers onunload.

I know JSLint doesn't like it. But then it doesn't like
document.write for the same reason (it claims they "might" be eval's.)

Some have said that it is unreasonable for JSLint to complain about
using the Function constructor for its intended purpose: constructing
functions. Some mumble about "nearly as bad as eval", which I take to
mean "have you thought about this thoroughly" and "aren't there less
resource intensive and more elegant methods available" - I guess
that's what JSLint is hinting at.

I have my own, but only push methods onto the cleanup stack if I know
they create circular references (eg associating a DOM element's events
with methods of an object that holds a reference to the element.)

There is always the generic "cycle over all DOM elements and set any
property whose typeof == function to null" quick-fix. :)


1. <URL: http://javascript.crockford.com/memory/leak.html >
 
D

David Mark

No. I didn't catch that specific problem. Setting the variables to
null won't help that?
The closure involves the activation object of the outer function
itself, only way to break it is to remove the handler, say by setting
it to null when the unload event occurs:
I do that when I am aware of creating circular references. In this
case, I see the circular reference, but can't understand why setting
the variable referencing the created element to null wouldn't break
the cycle.

Given:

function addClick(id){
var domNode = document.getElementById(id);
domNode.onclick = function(){...};

// Break circular reference
domNode = null;

}

Without that last statement, we have Richard's example:

"DOM_Node.onevent -> function_object.[[scope]] ->
scope_chain -> Activation_object.nodeRef -> DOM_Node"

Setting domNode to null inside the function removes the circular
reference, the chain is broken and as far as I can tell, you are
right: there is no leak (I've done a few tests that seem to confirm
it).

Glad to hear it.
That seems to agree with Douglas Crockford's article[1] on memory
leaks where he points out that circular references are the issue, not
closures. I also went back and did a search on Richard Cornford's
posts to clj and he says the same thing (repeatedly, actually :-x) - I
guess I was blind-sided by the association with closures.

You probably saw the one where I raised the same issue about
associating DOM events with object method. As long as the object
doesn't hold a reference to the element, it doesn't require cleanup.
Re-reading Richard's article I can see now what he was getting at:
when using closures and event handlers, it is common to create
circular references and leave them hanging around. Perhaps in the
conclusion about memory leaks he could include a something about
removing them by setting references to null where possible in addition
to the current advice about removing the handlers onunload.

I think that would make sense.
Some have said that it is unreasonable for JSLint to complain about
using the Function constructor for its intended purpose: constructing
functions. Some mumble about "nearly as bad as eval", which I take to
mean "have you thought about this thoroughly" and "aren't there less
resource intensive and more elegant methods available" - I guess
that's what JSLint is hinting at.

That's what I figured. I stopped using the Function constructor once
I discovered closures.
There is always the generic "cycle over all DOM elements and set any
property whose typeof == function to null" quick-fix. :)

I don't care for catch-alls as they seem like assertions that the
author didn't understand what the script did (usually a bad sign.)
 

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,982
Messages
2,570,190
Members
46,736
Latest member
zacharyharris

Latest Threads

Top