Collapsable list where text is in columns with headers (like a forum for example)

M

Mark

Hi

I'm looking to create a colapsable list like what we see in a news reader or
on a forum.

- Basically, any number of levels up to 10 (i.e. more than whats likely to
be needed)

- Able to expand/collapse each branch independantly

- 3 columns, where columns 2 and 3 are fixed width and aligned at all levels
with column headers (this is where it could get ugly as there may not be
room for column 1 if the record is more than a few levels deep.

I looked at bodging a tree control (namely
http://www.dynamicdrive.com/dynamicindex1/navigate1.htm) by dropping a 3x1
table within each li tag but a) the columns don't line up and b) I'm pretty
sure that was a really stupid idea ;o)

Anyone got an example or pointer to something similar?

Thanks

Mark
 
P

pr

Mark said:
I'm looking to create a colapsable list like what we see in a news reader or
on a forum.

- Basically, any number of levels up to 10 (i.e. more than whats likely to
be needed)

- Able to expand/collapse each branch independantly

- 3 columns, where columns 2 and 3 are fixed width and aligned at all levels
with column headers (this is where it could get ugly as there may not be
room for column 1 if the record is more than a few levels deep.

Fun programming challenge :)

The only way I could think of to line up cols 2 and 3 was to use a SPAN
for each and a CSS style per nesting level to counteract the indent of
each DIV. In a 'real' version you could stop columns overflowing by
setting a max width in CSS for the appropriate SPAN class(es) and using
overflow: hidden.

Anyway have a play:

<html>
<head>
<title>tree</title>
<style type="text/css">
.visible { display: block; }
.hidden { display : none; }
.closed { color: blue; }

div { position: relative; left: 1em; }
div:hover { cursor: pointer; }

.l1c1 { position: relative; left: 0; }
.l1c2 { position: relative; left: 15em; }
.l1c3 { position: relative; left: 25em; }

.l2c1 { position: relative; left: 0; }
.l2c2 { position: relative; left: 14em; }
.l2c3 { position: relative; left: 24em; }

.l3c1 { position: relative; left: 0; }
.l3c2 { position: relative; left: 13em; }
.l3c3 { position: relative; left: 23em; }
</style>

<script type="text/javascript">
function clicked(ev) {
var n, obj, c, visible = -1;

var isDOM2 = ev && (n = ev.target);
var isIE = window.event && (ev = window.event) &&
(n = ev.srcElement);

if (!n)
return true;

while (n && n.nodeName != "DIV") {
n = n.parentNode;
if (n.nodeType == 9)
return true;
}

if ((obj = n)) {
c = obj.firstChild;
while (c) {
if (c.nodeName == "DIV") {
if (visible < 0)
visible = (c.className != 'hidden');
c.className = (visible ? 'hidden' : 'visible');
}
c = c.nextSibling;
}
obj.className = (visible ? 'closed' : '');
if (isDOM2)
ev.stopPropagation();
else
ev.cancelBubble = true;
return false;
}
return true;
}

if (document.addEventListener)
document.addEventListener("click", clicked, false);
else if (document.onclick != "undefined")
document.onclick = clicked;
</script>
</head>
<body>
<div>
<span id="col1" class="l1c1">column 1</span>
<span id="col2" class="l1c2">column 2</span>
<span id="col3" class="l1c3">column 3</span>

<div>
<span id="col1" class="l2c1">column 1</span>
<span id="col2" class="l2c2">column 2</span>
<span id="col3" class="l2c3">column 3</span>
</div>

<div>
<span id="col1" class="l2c1">column 1</span>
<span id="col2" class="l2c2">column 2</span>
<span id="col3" class="l2c3">column 3</span>

<div>
<span id="col1" class="l3c1">column 1</span>
<span id="col2" class="l3c2">column 2</span>
<span id="col3" class="l3c3">column 3</span>
</div>

<div>
<span id="col1" class="l3c1">column 1</span>
<span id="col2" class="l3c2">column 2</span>
<span id="col3" class="l3c3">column 3</span>
</div>
</div>
</div>
</body>
</html>
 
M

Mark

Wow, thanks pr, I'll have a play indeed and let you know ho wit goes.

Thanks again


Mark
 
D

David Mark

Fun programming challenge :)

The only way I could think of to line up cols 2 and 3 was to use a SPAN
for each and a CSS style per nesting level to counteract the indent of
each DIV. In a 'real' version you could stop columns overflowing by
setting a max width in CSS for the appropriate SPAN class(es) and using
overflow: hidden.

Or you can use a table for what is clearly tabular data.
Anyway have a play:

<html>
<head>
<title>tree</title>
<style type="text/css">
.visible { display: block; }
.hidden { display : none; }
.closed { color: blue; }

div { position: relative; left: 1em; }
div:hover { cursor: pointer; }

.l1c1 { position: relative; left: 0; }
.l1c2 { position: relative; left: 15em; }
.l1c3 { position: relative; left: 25em; }

.l2c1 { position: relative; left: 0; }
.l2c2 { position: relative; left: 14em; }
.l2c3 { position: relative; left: 24em; }

.l3c1 { position: relative; left: 0; }
.l3c2 { position: relative; left: 13em; }
.l3c3 { position: relative; left: 23em; }
</style>

<script type="text/javascript">
function clicked(ev) {
var n, obj, c, visible = -1;

var isDOM2 = ev && (n = ev.target);
var isIE = window.event && (ev = window.event) &&
(n = ev.srcElement);

Don't use object inferences.
if (!n)
return true;

while (n && n.nodeName != "DIV") {
n = n.parentNode;
if (n.nodeType == 9)
return true;
}

if ((obj = n)) {
c = obj.firstChild;
while (c) {
if (c.nodeName == "DIV") {
if (visible < 0)
visible = (c.className != 'hidden');
c.className = (visible ? 'hidden' : 'visible');
}
c = c.nextSibling;
}

I tested this and it didn't work as you might have hoped.
obj.className = (visible ? 'closed' : '');
if (isDOM2)
ev.stopPropagation();
else
ev.cancelBubble = true;

Detect the stopPropagation method. Don't rely on the above-mentioned
inferences.
return false;
}
return true;
}

if (document.addEventListener)
document.addEventListener("click", clicked, false);
else if (document.onclick != "undefined")

Missing typeof.
document.onclick = clicked;
</script>
</head>
<body>
<div>
<span id="col1" class="l1c1">column 1</span>
<span id="col2" class="l1c2">column 2</span>
<span id="col3" class="l1c3">column 3</span>

<div>
<span id="col1" class="l2c1">column 1</span>
<span id="col2" class="l2c2">column 2</span>
<span id="col3" class="l2c3">column 3</span>
</div>

<div>
<span id="col1" class="l2c1">column 1</span>
<span id="col2" class="l2c2">column 2</span>
<span id="col3" class="l2c3">column 3</span>

<div>
<span id="col1" class="l3c1">column 1</span>
<span id="col2" class="l3c2">column 2</span>
<span id="col3" class="l3c3">column 3</span>
</div>

<div>
<span id="col1" class="l3c1">column 1</span>
<span id="col2" class="l3c2">column 2</span>
<span id="col3" class="l3c3">column 3</span>
</div>
</div>
</div>
</body>
</html>

This also has accessibility issues. Showing all nodes expanded from
the start helps with that but hinders usability.

Here's a basic example. In production code you probably wouldn't
define the hierarchy based on element ID's and you would use
background images for the +/- indicators (IE does not support content
added with CSS.) It was tested briefly with the usual suspects.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/
TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Collapsible Posts</title>
<style type="text/css" media="all">
table.posts { border:none;width:100%;border-collapse:collapse }
table.posts td { width:33%;white-space:nowrap }
table.posts th { text-align:left }
table.posts th, table.posts tr:hover, table.posts tr:hover a
{ background-color:#DDDDDD;color:black }
table.posts tr.shownParent:hover, table.posts tr.shownParent:hover a
{ background-color:#0000DD;color:white }
td.indent1 { padding-left:1em }
td.indent2 { padding-left:2em }
td.indent3 { padding-left:3em }
a.open:before { content: "- " }
a.closed:before { content: "+ " }
tr.shown, tr.shownParent { display:block;display:table-row }
tr.hidden { display:none }
</style>
<style type="text/css" media="handheld">
td.indent1 { padding-left:.5em }
td.indent2 { padding-left:1em }
td.indent3 { padding-left:2em }
</style>
<script type="text/javascript">
var global = this;
if (this.document && this.document.getElementsByTagName &&
this.document.getElementById) {
(function() {
var html = global.document.getElementsByTagName('html');
if (html && html[0] && html[0].style &&
typeof(html[0].style.visibility) == 'string') { document.write('<style
type="text/css" media="all">#mydiscussion { visibility:hidden }<\/
style>'); }
})();

this.onload = function() {
var doc = global.document;
var el, anchors, postId, posts, i, l;

function findPosts(postId) {
var j = 1;
var childPosts = [];
var elChild = doc.getElementById(postId + 'p' + j);
while (elChild) {
childPosts.push(elChild);
elChild = doc.getElementById(postId + 'p' + (++j));
}
return childPosts;
}

function showPosts(posts, b, cascade) {
var j = 0;
var m = posts.length;
var title, childPosts;
while (j < m) {
posts[j].className = (b)?'shown':'hidden';
childPosts = findPosts(posts[j].id);
if (childPosts.length) {
if (b) { posts[j].className = 'shownParent'; }
title = document.getElementById(posts[j].id + 'Title');
if (title) {
if (title.className == 'open' || cascade) {
showPosts(childPosts, b, cascade);
}
if (cascade) { title.className = (b)?'open':'closed'; }
}
}
j++;
}
}

el = document.getElementById('mydiscussion');
if (el && el.style && typeof(el.style.display) == 'string') {
anchors = el.getElementsByTagName('a');
i = 0;
l = anchors.length;
while (i < l) {
if (anchors.id && typeof(anchors.href) != 'unknown' && !
anchors.href) {
postId = anchors.id.substring(0, anchors.id.length - 5);
posts = findPosts(postId);
if (posts.length) {
var elPost = document.getElementById(postId);
if (elPost) {
if (anchors.className != 'closed') {
showPosts(posts, false, true);
anchors.className = 'closed';
}
anchors.href = '#';
anchors.tabIndex = 0;
if (!elPost.className) { elPost.className = 'shownParent'; }
elPost.style.cursor = 'pointer';
elPost.onclick = anchors.onclick = (function(posts, el)
{ var b; return function(e) { e = e || global.event; b = (el.className
== 'closed'); el.className = (b)?'open':'closed'; showPosts(posts, b);
if (e.stopPropagation) { e.stopPropagation(); } e.cancelBubble = true;
return false; }; })(posts, anchors);
}
}
}
i++;
}
el.style.visibility = 'visible';
}
};
}
</script>
</head>
<body>
<table id="mydiscussion" class="posts">
<tbody>
<tr><th>Heading 1</th><th>Heading 2</th><th>Heading 3</th></tr>
<tr id="p1"><td><a id="p1Title">Column 1</a></td><td>Column 2</
td><td>Column 3</td></tr>
<tr id="p1p1"><td class="indent1"><a id="p1p1Title">Column 1</a></
td><td>Column 2</td><td>Column 3</td></tr>
<tr id="p1p2"><td class="indent1"><a id="p1p2Title">Column 1</a></
td><td>Column 2</td><td>Column 3</td></tr>
<tr id="p1p2p1"><td class="indent2"><a id="p1p2p1Title">Column 1</a></
td><td>Column 2</td><td>Column 3</td></tr>
<tr id="p1p2p2"><td class="indent2"><a id="p1p2p2Title">Column 1</a></
td><td>Column 2</td><td>Column 3</td></tr>
<tr id="p1p2p2p1"><td class="indent3"><a id="p1p2p2p1Title">Column 1</
a></td><td>Column 2</td><td>Column 3</td></tr>
<tr id="p1p2p3"><td class="indent2"><a id="p1p2p3Title">Column 1</a></
td><td>Column 2</td><td>Column 3</td></tr>
<tr id="p1p2p3p1"><td class="indent3"><a id="p1p2p1p1Title">Column 1</
a></td><td>Column 2</td><td>Column 3</td></tr>
<tr id="p2"><td><a id="p2Title">Column 1</a></td><td>Column 2</
td><td>Column 3</td></tr>
</tbody>
</table>
</body>
</html>
 
D

David Mark

Oops. Left out the memory leak cleanup.

global.onunload = function() {
i = 0;
l = anchors.length;
while (i < l) { anchors[i++].onclick = null; }
};

Goes right before this line:

el.style.visibility = 'visible';
 
M

Mark

Thanks Mark

I'll take a look today. I had given up on the idea of column alignment for
columns 2 and 3 as there were issues with overflow text etc.

Thanks for the memory cleanup bit as well. I've been programming for years
but rarely for the web so I would have missed that one till it bit me on the
backside.

Thanks again

Mark
 
M

Mark

Hi

For the record, if anyones trying to do something similar, I made the
position properties of the styles for columns 2 and 3 absolute in order to
keep them aligned when column 1 had different length strings.

Mark
 
M

Mark

Hello again

This works great (thanks), the only issue I got though is that in firefox
long strings don't wrap, i.e. the cell height remains constant and the table
gets wider. I even tried fixing the widths of the table and cells (in
pixels), still doesn't wrap) and then dropped the whole table inside another
fixed width table and it still gets wider rather than wrapping.

Work fine in IE6 though, will keep plugging at the firefox issue.

Cheers

Mark


David Mark said:
Fun programming challenge :)

The only way I could think of to line up cols 2 and 3 was to use a SPAN
for each and a CSS style per nesting level to counteract the indent of
each DIV. In a 'real' version you could stop columns overflowing by
setting a max width in CSS for the appropriate SPAN class(es) and using
overflow: hidden.

Or you can use a table for what is clearly tabular data.
Anyway have a play:

<html>
<head>
<title>tree</title>
<style type="text/css">
.visible { display: block; }
.hidden { display : none; }
.closed { color: blue; }

div { position: relative; left: 1em; }
div:hover { cursor: pointer; }

.l1c1 { position: relative; left: 0; }
.l1c2 { position: relative; left: 15em; }
.l1c3 { position: relative; left: 25em; }

.l2c1 { position: relative; left: 0; }
.l2c2 { position: relative; left: 14em; }
.l2c3 { position: relative; left: 24em; }

.l3c1 { position: relative; left: 0; }
.l3c2 { position: relative; left: 13em; }
.l3c3 { position: relative; left: 23em; }
</style>

<script type="text/javascript">
function clicked(ev) {
var n, obj, c, visible = -1;

var isDOM2 = ev && (n = ev.target);
var isIE = window.event && (ev = window.event) &&
(n = ev.srcElement);

Don't use object inferences.
if (!n)
return true;

while (n && n.nodeName != "DIV") {
n = n.parentNode;
if (n.nodeType == 9)
return true;
}

if ((obj = n)) {
c = obj.firstChild;
while (c) {
if (c.nodeName == "DIV") {
if (visible < 0)
visible = (c.className != 'hidden');
c.className = (visible ? 'hidden' : 'visible');
}
c = c.nextSibling;
}

I tested this and it didn't work as you might have hoped.
obj.className = (visible ? 'closed' : '');
if (isDOM2)
ev.stopPropagation();
else
ev.cancelBubble = true;

Detect the stopPropagation method. Don't rely on the above-mentioned
inferences.
return false;
}
return true;
}

if (document.addEventListener)
document.addEventListener("click", clicked, false);
else if (document.onclick != "undefined")

Missing typeof.
document.onclick = clicked;
</script>
</head>
<body>
<div>
<span id="col1" class="l1c1">column 1</span>
<span id="col2" class="l1c2">column 2</span>
<span id="col3" class="l1c3">column 3</span>

<div>
<span id="col1" class="l2c1">column 1</span>
<span id="col2" class="l2c2">column 2</span>
<span id="col3" class="l2c3">column 3</span>
</div>

<div>
<span id="col1" class="l2c1">column 1</span>
<span id="col2" class="l2c2">column 2</span>
<span id="col3" class="l2c3">column 3</span>

<div>
<span id="col1" class="l3c1">column 1</span>
<span id="col2" class="l3c2">column 2</span>
<span id="col3" class="l3c3">column 3</span>
</div>

<div>
<span id="col1" class="l3c1">column 1</span>
<span id="col2" class="l3c2">column 2</span>
<span id="col3" class="l3c3">column 3</span>
</div>
</div>
</div>
</body>
</html>

This also has accessibility issues. Showing all nodes expanded from
the start helps with that but hinders usability.

Here's a basic example. In production code you probably wouldn't
define the hierarchy based on element ID's and you would use
background images for the +/- indicators (IE does not support content
added with CSS.) It was tested briefly with the usual suspects.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/
TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Collapsible Posts</title>
<style type="text/css" media="all">
table.posts { border:none;width:100%;border-collapse:collapse }
table.posts td { width:33%;white-space:nowrap }
table.posts th { text-align:left }
table.posts th, table.posts tr:hover, table.posts tr:hover a
{ background-color:#DDDDDD;color:black }
table.posts tr.shownParent:hover, table.posts tr.shownParent:hover a
{ background-color:#0000DD;color:white }
td.indent1 { padding-left:1em }
td.indent2 { padding-left:2em }
td.indent3 { padding-left:3em }
a.open:before { content: "- " }
a.closed:before { content: "+ " }
tr.shown, tr.shownParent { display:block;display:table-row }
tr.hidden { display:none }
</style>
<style type="text/css" media="handheld">
td.indent1 { padding-left:.5em }
td.indent2 { padding-left:1em }
td.indent3 { padding-left:2em }
</style>
<script type="text/javascript">
var global = this;
if (this.document && this.document.getElementsByTagName &&
this.document.getElementById) {
(function() {
var html = global.document.getElementsByTagName('html');
if (html && html[0] && html[0].style &&
typeof(html[0].style.visibility) == 'string') { document.write('<style
type="text/css" media="all">#mydiscussion { visibility:hidden }<\/
style>'); }
})();

this.onload = function() {
var doc = global.document;
var el, anchors, postId, posts, i, l;

function findPosts(postId) {
var j = 1;
var childPosts = [];
var elChild = doc.getElementById(postId + 'p' + j);
while (elChild) {
childPosts.push(elChild);
elChild = doc.getElementById(postId + 'p' + (++j));
}
return childPosts;
}

function showPosts(posts, b, cascade) {
var j = 0;
var m = posts.length;
var title, childPosts;
while (j < m) {
posts[j].className = (b)?'shown':'hidden';
childPosts = findPosts(posts[j].id);
if (childPosts.length) {
if (b) { posts[j].className = 'shownParent'; }
title = document.getElementById(posts[j].id + 'Title');
if (title) {
if (title.className == 'open' || cascade) {
showPosts(childPosts, b, cascade);
}
if (cascade) { title.className = (b)?'open':'closed'; }
}
}
j++;
}
}

el = document.getElementById('mydiscussion');
if (el && el.style && typeof(el.style.display) == 'string') {
anchors = el.getElementsByTagName('a');
i = 0;
l = anchors.length;
while (i < l) {
if (anchors.id && typeof(anchors.href) != 'unknown' && !
anchors.href) {
postId = anchors.id.substring(0, anchors.id.length - 5);
posts = findPosts(postId);
if (posts.length) {
var elPost = document.getElementById(postId);
if (elPost) {
if (anchors.className != 'closed') {
showPosts(posts, false, true);
anchors.className = 'closed';
}
anchors.href = '#';
anchors.tabIndex = 0;
if (!elPost.className) { elPost.className = 'shownParent'; }
elPost.style.cursor = 'pointer';
elPost.onclick = anchors.onclick = (function(posts, el)
{ var b; return function(e) { e = e || global.event; b = (el.className
== 'closed'); el.className = (b)?'open':'closed'; showPosts(posts, b);
if (e.stopPropagation) { e.stopPropagation(); } e.cancelBubble = true;
return false; }; })(posts, anchors);
}
}
}
i++;
}
el.style.visibility = 'visible';
}
};
}
</script>
</head>
<body>
<table id="mydiscussion" class="posts">
<tbody>
<tr><th>Heading 1</th><th>Heading 2</th><th>Heading 3</th></tr>
<tr id="p1"><td><a id="p1Title">Column 1</a></td><td>Column 2</
td><td>Column 3</td></tr>
<tr id="p1p1"><td class="indent1"><a id="p1p1Title">Column 1</a></
td><td>Column 2</td><td>Column 3</td></tr>
<tr id="p1p2"><td class="indent1"><a id="p1p2Title">Column 1</a></
td><td>Column 2</td><td>Column 3</td></tr>
<tr id="p1p2p1"><td class="indent2"><a id="p1p2p1Title">Column 1</a></
td><td>Column 2</td><td>Column 3</td></tr>
<tr id="p1p2p2"><td class="indent2"><a id="p1p2p2Title">Column 1</a></
td><td>Column 2</td><td>Column 3</td></tr>
<tr id="p1p2p2p1"><td class="indent3"><a id="p1p2p2p1Title">Column 1</
a></td><td>Column 2</td><td>Column 3</td></tr>
<tr id="p1p2p3"><td class="indent2"><a id="p1p2p3Title">Column 1</a></
td><td>Column 2</td><td>Column 3</td></tr>
<tr id="p1p2p3p1"><td class="indent3"><a id="p1p2p1p1Title">Column 1</
a></td><td>Column 2</td><td>Column 3</td></tr>
<tr id="p2"><td><a id="p2Title">Column 1</a></td><td>Column 2</
td><td>Column 3</td></tr>
</tbody>
</table>
</body>
</html>
 
P

pr

Mark said:
This works great (thanks), the only issue I got though is that in firefox
long strings don't wrap, i.e. the cell height remains constant and the table
gets wider. I even tried fixing the widths of the table and cells (in
pixels), still doesn't wrap) and then dropped the whole table inside another
fixed width table and it still gets wider rather than wrapping.

Comment out or remove 'white-space:nowrap' from the 'table.posts td' CSS.
 
D

David Mark

Hello again

This works great (thanks), the only issue I got though is that in firefox
long strings don't wrap, i.e. the cell height remains constant and the table
gets wider. I even tried fixing the widths of the table and

This was by design.

cells (in
pixels), still doesn't wrap) and then dropped the whole table inside another
fixed width table and it still gets wider rather than wrapping.

Work fine in IE6 though, will keep plugging at the firefox issue.

You mean the cells did wrap in IE6? That's surprising to me. I
didn't test it in IE6, but I thought the CSS I used would work the
same in IE6 as IE7 (except for the tr:hover rule.) I did just notice
that the headers are wrapping as I left out a white-space rule for
them.
 
M

Mark

Cool... I pulled out the white-space rule and everything wraps in FF and IE
now.

IE6 was ok before, not sure why. If I get time I'll put the white-space rule
back and see what happens.

Cheers

Mark
 
D

David Mark

Cool... I pulled out the white-space rule and everything wraps in FF and IE
now.

If that is how you want it. Personally, I don't like table cells to
wrap.
IE6 was ok before, not sure why. If I get time I'll put the white-space rule
back and see what happens.

Don't worry about it. I'll get around to trying this in IE6. I'd be
interested to see if table cells in IE6 ignore the white-space rule.

BTW, the handheld style had a typo. Change this:

td.indent3 { padding-left:1.5em }

Also, document.write should be global.document.write. Additionally, I
left in a few references to "document" that should have been changed
to "doc." None of this will cause a problem, but should be changed to
be consistent with the rest of the code.
 
M

Mark

Cheers David

I wanted to allow wrapping as the string lengths can be quite long and I'm
not keen on horizontal scrollbars.

Top solution btw

Mark
 
D

David Mark

Cheers David

I wanted to allow wrapping as the string lengths can be quite long and I'm
not keen on horizontal scrollbars.

I don't care for horizontal scrollbars either. So for long-winded
table cell text, wrapping makes sense.
Top solution btw

Thanks.
 
D

David Mark

Cheers David
[snip]

I went to turn this into an object, which required removing the hide-
while-loading logic and noticed a mistake. This line:

el.style.visibility = 'visible';

It's nested one level too deep. Move it out one level and change it
to:

if (el) { el.style.visibility = 'visible'; }

Also, this widget should be used with a print style sheet. Two
actually as it exposes yet another CSS bug in IE.

<style type="text/css" media="print">
tr.hidden { display:table-row }
a.open, a.closed { text-decoration:none;color:black;background-
color:inherit }
a.open:before { content: "" }
a.closed:before { content: "" }
</style>
<!--[if IE]>
<style type="text/css" media="print">
tr.hidden { display:block }
</style>
<![endif]-->
 
B

beegee

Oops. Left out the memory leak cleanup.

global.onunload = function() {
i = 0;
l = anchors.length;
while (i < l) { anchors[i++].onclick = null; }

};

Goes right before this line:

el.style.visibility = 'visible';

David, thanks to your sample code post I have now lowered my self
evaluation of my javascript skills by 2 points. ;) Could you tell me
what memory leak you're referring to? I thought garbage collection
would pick this stuff off.

Bob
 
D

David Mark

On Sep 26, 4:26 pm, David Mark <[email protected]> wrote:
Oops. Left out the memory leak cleanup.
global.onunload = function() {
i = 0;
l = anchors.length;
while (i < l) { anchors[i++].onclick = null; }

Goes right before this line:
el.style.visibility = 'visible';

David, thanks to your sample code post I have now lowered my self
evaluation of my javascript skills by 2 points. ;) Could you
tell me

I've lowered my own several points based on the post from late last
night. I must have been really tired when I came up with that one.
Change the correction to:

if (el && el.style) { el.style.visibility = 'visible'; }

Moving the line out a level meant that the style property was not
assured.
what memory leak you're referring to? I thought garbage collection
would pick this stuff off.

IE has a bug where closures that create circular references involving
DOM objects are not garbage collected.

http://www.jibbering.com/faq/faq_notes/closures.html

Read this line carefully and you will see that every anchor will leak
the code in its onclick event. The solution is to break the circular
references when the page is unloaded.

elPost.onclick = anchors.onclick = (function(posts, el) { var b;
return function(e) { e = e || global.event; b = (el.className ==
'closed'); el.className = (b)?'open':'closed'; showPosts(posts, b); if
(e.stopPropagation) { e.stopPropagation(); } e.cancelBubble = true;
return false; }; })(posts, anchors);

In short, the onclick property of anchors is an anonymous function
which creates a closure referencing anchors as el.
 

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

Forum statistics

Threads
473,995
Messages
2,570,236
Members
46,823
Latest member
Nadia88

Latest Threads

Top