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>