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

Discussion in 'Javascript' started by Mark, Sep 25, 2007.

  1. Mark

    Mark Guest

    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
     
    Mark, Sep 25, 2007
    #1
    1. Advertising

  2. Mark

    pr Guest

    Re: Collapsable list where text is in columns with headers (likea forum for example)

    Mark wrote:
    > 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>
     
    pr, Sep 25, 2007
    #2
    1. Advertising

  3. Mark

    Mark Guest

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

    Thanks again


    Mark

    "pr" <> wrote in message
    news:cY8Ki.301$...
    > Mark wrote:
    >> 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>
     
    Mark, Sep 25, 2007
    #3
  4. Mark

    David Mark Guest

    On Sep 25, 10:30 am, pr <> wrote:
    > Mark wrote:
    > > 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.


    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>
     
    David Mark, Sep 26, 2007
    #4
  5. Mark

    David Mark Guest

    On Sep 26, 4:26 pm, David Mark <> 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 Mark, Sep 27, 2007
    #5
  6. Mark

    Mark Guest

    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

    "David Mark" <> wrote in message
    news:...
    > On Sep 26, 4:26 pm, David Mark <> 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';
    >
    >
     
    Mark, Sep 27, 2007
    #6
  7. Mark

    Mark Guest

    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


    "pr" <> wrote in message
    news:cY8Ki.301$...
    > Mark wrote:
    >> 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>
     
    Mark, Sep 27, 2007
    #7
  8. Mark

    Mark Guest

    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" <> wrote in message
    news:...
    > On Sep 25, 10:30 am, pr <> wrote:
    >> Mark wrote:
    >> > 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.

    >
    > 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>
    >
     
    Mark, Sep 27, 2007
    #8
  9. Mark

    pr Guest

    Re: Collapsable list where text is in columns with headers (likea forum for example)

    David Mark wrote:
    [a sample application]

    Good one.
     
    pr, Sep 27, 2007
    #9
  10. Mark

    pr Guest

    Re: Collapsable list where text is in columns with headers (likea forum for example)

    Mark wrote:
    > 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.
     
    pr, Sep 27, 2007
    #10
  11. Mark

    Mark Guest

    Excellent, thanks pr

    "pr" <> wrote in message
    news:JCMKi.41294$...
    > Mark wrote:
    >> 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.
     
    Mark, Sep 27, 2007
    #11
  12. Mark

    David Mark Guest

    On Sep 27, 3:10 am, "Mark" <> wrote:
    > 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.
     
    David Mark, Sep 27, 2007
    #12
  13. Mark

    David Mark Guest

    On Sep 27, 7:31 am, pr <> wrote:
    > David Mark wrote:
    >
    > [a sample application]
    >
    > Good one.


    Thanks. It turned out better than I thought it would.
     
    David Mark, Sep 27, 2007
    #13
  14. Mark

    Mark Guest

    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

    "David Mark" <> wrote in message
    news:...
    > On Sep 27, 3:10 am, "Mark" <> wrote:
    >> 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.
    >
     
    Mark, Sep 27, 2007
    #14
  15. Mark

    David Mark Guest

    On Sep 27, 2:03 pm, "Mark" <> wrote:
    > 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.
     
    David Mark, Sep 27, 2007
    #15
  16. Mark

    Mark Guest

    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

    "David Mark" <> wrote in message
    news:...
    > On Sep 27, 2:03 pm, "Mark" <> wrote:
    >> 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.
    >
    >
     
    Mark, Sep 27, 2007
    #16
  17. Mark

    David Mark Guest

    On Sep 27, 3:10 pm, "Mark" <> wrote:
    > 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.
     
    David Mark, Sep 27, 2007
    #17
  18. Mark

    David Mark Guest

    On Sep 27, 3:10 pm, "Mark" <> wrote:
    > 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]-->
     
    David Mark, Sep 28, 2007
    #18
  19. Mark

    beegee Guest

    On Sep 26, 9:54 pm, David Mark <> wrote:
    > On Sep 26, 4:26 pm, David Mark <> 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
    what memory leak you're referring to? I thought garbage collection
    would pick this stuff off.

    Bob
     
    beegee, Sep 28, 2007
    #19
  20. Mark

    David Mark Guest

    On Sep 28, 10:09 am, beegee <> wrote:
    > On Sep 26, 9:54 pm, David Mark <> wrote:
    >
    > > On Sep 26, 4:26 pm, David Mark <> 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.
     
    David Mark, Sep 28, 2007
    #20
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Brian Henry

    collapsable lists?

    Brian Henry, Mar 30, 2005, in forum: ASP .Net
    Replies:
    1
    Views:
    405
    Joseph Byrns
    Mar 31, 2005
  2. Rlrcstr

    Collapsable paragraphs...

    Rlrcstr, May 16, 2005, in forum: ASP .Net
    Replies:
    7
    Views:
    722
    Rlrcstr
    May 17, 2005
  3. Replies:
    2
    Views:
    452
    Andy Dingley
    May 20, 2005
  4. Savvoulidis Iordanis
    Replies:
    1
    Views:
    2,209
    Michael Nemtsev [MVP]
    Feb 23, 2008
  5. Replies:
    2
    Views:
    1,287
    Michele Dondi
    May 18, 2007
Loading...

Share This Page