Click here to Skip to main content
Licence 
First Posted 1 Jul 2006
Views 98,425
Bookmarked 100 times

JavaScript Horizontal Tree

By | 1 Jul 2006 | Article
Horizontal treeview for JavaScript.

Sample Image - Horizontal Tree.png

Introduction

Recently, I had a task to make a horizontal tree using JavaScript. I thought that I would find millions of examples for a tree on the internet, but I didn't find anything useful. We all know the vertical tree which is the most commonly available on the internet, as it's used for many scenarios like navigation menus etc., but what if we wanted to represent the info in a horizontal tree format, which will help the user to have a more intensive view for the data on the tree? I didn't want to waste time building something from scratch, so I've took the code here and developed another version from it to draw a horizontal tree.

Using the code

The main idea is that instead of building tree nodes as a DIV under DIV, we will use table and table cells to represent a level of node, so whenever I start a new level, I will draw a new table, and for every node, I will put it in a table cell, for example:

str += ' <td valign=top align=center > '; 
str += this.node(cn, n);
str += ' </td> ';

Now, I think I should explain what the biggest problem I had when I developed this script, it's the lines between the nodes. So I started with a trivial idea or solution which is putting a border-top style for the table cell that contains a child node, and then I had to go with this idea and split each table cell that contains a child node into a new table with two rows: one for the lines and the other for the node text, icon and plus/minus button if it exists. The first row in that table is split into three cells: the first cell should have a border top in any of these two cases:

  1. the child node is a rightmost node.
  2. the child node is a middle node.

The second cell will contain a vertical line, and this line should be displayed in any case. The third cell should have a border top in any of these two cases:

  1. The child node is a leftmost node.
  2. The child node is a middle node.

So, I had to add three new properties to the tree node object: one if this node is a leftmost node or not, the second if this node is a rightmost node or not, and the third if the node is the only child or not. If the three values are false, then this node is a middle node. I had to keep track of how much children every parent has, and the index of the node between the parent and the children, and based on these numbers, I could decide which node is at the very left, which is at the very right, and which is an only child. And, here's the most effective part of the code:

// Creates the tree structure 
dTree.prototype.addNode = function(pNode) 
{ 
    var str = ''; 
    str += '<table border=0 ' + 
           'cellpadding=0 cellspacing=0 >'; 
    str += ' <tr>'; 
    var n=0; 
    if (this.config.inOrder) 
        n = pNode._ai; 

    var childsIndex =0;
    
    for (n; n<this.aNodes.length; n++)
    // loop through child nodes of each parent
    { 
        if (this.aNodes[n].pid == pNode.id) 
        { 
        var cn = this.aNodes[n]; 
        cn._p = pNode; cn._ai = n; 
        this.setCS(cn); 
        // Checks if a node has any children
        // and if it is the last sibling 
        cn._childIndex = childsIndex++;
        // set the index of the node
        // among its parent childs

        if(cn._p._children <= 1)
        // if this is the only child
            cn._isOnly; 
        else 
        { 
            if(cn._childIndex == 0)
            // if it's the very left node 
                cn._isLeft=true; 
            if(cn._childIndex == cn._p._children-1)
            // if it's the very right node 
                cn._isRight=true; 
        } 
        if (!cn.target && this.config.target) 
            cn.target = this.config.target; 
        if (cn._hc && !cn._io && this.config.useCookies) 
            cn._io = this.isOpen(cn.id); 
        if (!this.config.folderLinks && cn._hc) 
            cn.url = null; 
        if (this.config.useSelection && cn.id == 
            this.selectedNode && !this.selectedFound) 
        { 
            cn._is = true; this.selectedNode = n; 
            this.selectedFound = true; 
        } 
        str += ' <td valign=top align=center > '; 
        str += this.node(cn, n); 
        str += ' </td> '; 
        
        if (cn._ls) 
            break; 
        } 
    } 
    str += ' </tr>';
    str += '</table>';

    return str; 
    }; 

// Creates the node icon, url and text 
dTree.prototype.node = function(node, nodeId) 
{ 

    if(node._p._children <= 1)
        node._isOnly=true;

    var str = '<div class="dTreeNode" style="white-space:nowrap">'; 
    str += '<table border="0" cellpadding="0" ' + 
           'cellspacing="0" width="100%" >';
    str += '<tr>'; // first level of lines
    str += ' <td align="center" width="52%" ';
    
    if (this.root.id != node.pid) // if it's not a root node 
    { 
        if(this.config.useLines) // if we using line
        { 
            if(node._isOnly) // if it's the only child 
            { 
                str += ''; 
            } 
            else if(node._isLeft) // if it's the very left node 
            { 
                str += ''; 
            } 
            else if(node._isRight) // if it's the very right node 
            { 
                str += ' style="border-top-width:1px;' + 
                       'border-top-style:dotted;' + 
                       'border-top-color:Gray;" '; 
            } 
            else // if it's the middle node 
            { 
                str += ' style="border-top-width:1px;border' + 
                       '-top-style:dotted;border-top-color:Gray;" '; 
            } 
        } 
    } 
    str += ' >&nbsp; '; 

    str += ' </td>';
    str += ' <td valign="top" style="padding-top:0px;" ' + 
           'align="center" width="1%" ';
    str += ' >'; 
    
    if (this.root.id != node.pid)
    /// if this node isn't a first level node 
    { 
        str += '<img src="'; 
        if(this.config.useLines) 
        { 
            str += this.icon.line;
        } 
        else 
        { 
            str += this.icon.empty; 
        } 
        str += '" alt="" />'; 
    }


    str += ' </td>'; 
    str += ' <td align="center" width="52%" '; 

    if (this.root.id != node.pid) 
    { 
        if(this.config.useLines) 
        { 
            if(node._isOnly) 
                str += ' '; 
            else if(node._isLeft) 
                str += ' style="border-top-width:1px;border' + 
                       '-top-style:dotted;border-top-color:Gray;" '; 
            else if(node._isRight) 
                str += ''; 
            else 
                str += ' style="border-top-width:1px;border' + 
                       '-top-style:dotted;border-top-color:Gray;" '; 
        } 
    } 
    str += ' >&nbsp; '; 
    str += ' </td>'; 
    str += '</tr>'; 

    str += '<tr>'; 
    str += ' <td align="center" colspan="3">'; 
    str += '<table border=0 cellpadding="0" cellspacing="0" >'; 
    str += '<tr><td valign=top align=center>' + 
           this.indent(node, nodeId); 
    
    if (this.config.useIcons) 
    { 
        if (!node.icon) 
            node.icon = (this.root.id == node.pid) ? 
                         this.icon.root : ((node._hc) ? 
                         this.icon.folder : this.icon.node); 
        if (!node.iconOpen) 
            node.iconOpen = (node._hc) ? 
                             this.icon.folderOpen : 
                             this.icon.node; 
        if (this.root.id == node.pid) 
        { 
            node.icon = this.icon.root; 
            node.iconOpen = this.icon.root; 
        } 
        
        str += '<img id="i' + this.obj + nodeId + '" src="' + 
               ((node._io) ? node.iconOpen : node.icon) + '" alt="" />'; 
    } 
    str+="</td></tr><tr><td valign=top" + 
         " align=center style='white-space:nowrap'>";

    if (node.url) 
    { 
        str += '<a id="s' + this.obj + nodeId + '" class="' + 
               ((this.config.useSelection) ? ((node._is ? 'nodeSel' : 
                 'node')) : 'node') + '" href="' + node.url + '"'; 
        if (node.title) 
            str += ' title="' + node.title + '"'; 
        if (node.target) 
            str += ' target="' + node.target + '"'; 
        if (this.config.useStatusText) 
            str += ' onmouseover="window.status=\'' + node.name + 
                   '\';return true;" ' + 
                   'onmouseout="window.status=\'\';return true;" '; 
        if (this.config.useSelection && ((node._hc && 
            this.config.folderLinks) || !node._hc)) 
            str += ' onclick="javascript: ' + this.obj + 
                   '.s(' + nodeId + ');"'; 
        str += '>'; 
    } 
    else if ((!this.config.folderLinks || !node.url) && 
               node._hc && node.pid != this.root.id) 
            str += '<a href="javascript: ' + this.obj + 
                   '.o(' + nodeId + ');" class="node">'; 

    str += node.name;
    if (node.url || ((!this.config.folderLinks || 
                      !node.url) && node._hc)) 
    str += '</a>'; 
    str += '</div>'; 
    str += '</td></tr></table>'; 
    str += '</td></tr></table>'; 

    if (node._hc) // if this node have children
    { 
        str += '<div id="d' + this.obj + nodeId + '" class="clip" ' + 
               'style="display:' + ((node._io) ? 'block' : 'none') + ';">'; 
        str += '<table border=0 cellpadding=0 cellspacing=0>';
        str += '<tr><td height="9" align="center"><img src="' + 
               this.icon.smallLine+'" alt="" border=0></td></tr>';
        str += '<tr><td>';

        str += this.addNode(node); 

        str += '</td></tr></table>';
        str += '</div>'; 
    } 
    this.aIndent.pop(); 
    return str; 
};

Points of interest

The most important thing I learned from this script is how much recursion can be effective in JavaScript. Also, it was a very good experiment to go with a trivial idea to build good lines in a tree. I think it's pretty stable now.

History

This is my first article.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Mahmoud Tahoon

Web Developer

Egypt Egypt

Member



Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
QuestionJavaScript Horizontal Tree PinmemberBiswa_c2:33 23 Apr '12  
BugConnection line can not be aligned when nodes is too many Pinmembermaxmas21:14 22 Feb '12  
GeneralMy vote of 5 PinmemberMember 78677120:55 20 Jun '11  
Questionhorizontal tree with javascript PinmemberBadari Narayana18:32 12 Jan '11  
GeneralRe: horizontal tree with javascript PinmemberBassam Abdul-Baki3:04 18 Jan '11  
GeneralList items instead of tables Pinmemberanand sueman1:15 14 Dec '10  
GeneralFamily Tree PinmemberBahraini023:44 15 Apr '10  
QuestionHow to generate dynamic node from the database in horizontal tree view. please help me Pinmemberritesh suhane19:20 1 Mar '10  
QuestionHowto dynamicly add a node to the tree Pinmemberhacking_mike1:24 27 Aug '09  
AnswerRe: Howto dynamicly add a node to the tree Pinmemberhacking_mike4:48 28 Aug '09  
GeneralPlease Help Pinmemberluo_fen_fang19:16 21 May '09  
GeneralHuge Data Problem. Pinmemberhymns5:24 27 Apr '09  
Generalthanks very much Pinmember21cn_zy16:38 24 Jun '08  
thanks very much
GeneralGreat work, but look this Pinmemberzenhaust12:47 27 Apr '08  
GeneralVertical Menu limit PinmemberBoon8218:33 30 Dec '07  
GeneralRe: Vertical Menu limit Pinmembernina_lyy17:22 15 May '08  
GeneralRe: Vertical Menu limit Pinmembergirishoak18:44 4 Jan '09  
GeneralRe: Vertical Menu limit Pinmembersuperb123:14 2 May '11  
Generalgreat work Pinmemberadi_9991:33 24 Sep '07  
Questionfunctioning of the code Pinmemberseralan4:53 27 Jul '07  
AnswerRe: functioning of the code Pinmemberadi_99921:25 4 Oct '07  
QuestionPrint Problem Pinmembershelal22:45 16 Aug '06  
AnswerRe: Print Problem Pinmemberharidip19:29 14 Jun '09  
GeneralMy Example [modified*3] PinmemberBassam Abdul-Baki9:45 7 Aug '06  
GeneralRe: My Example PinmemberBassam Abdul-Baki9:47 10 Aug '06  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web04 | 2.5.120604.1 | Last Updated 1 Jul 2006
Article Copyright 2006 by Mahmoud Tahoon
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid