Click here to Skip to main content
Click here to Skip to main content

JavaScript Horizontal Tree

, 1 Jul 2006
Rate this:
Please Sign up or sign in to vote.
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

Share

About the Author

Mahmoud Tahoon
Web Developer
Egypt Egypt
No Biography provided

Comments and Discussions

 
QuestionWordpress Plugin PinmemberMember 1089901622-Jun-14 0:43 
QuestionThanks for such a great work PinmemberMember 1043622425-Mar-14 22:59 
QuestionJavaScript Horizontal Tree PinmemberBiswa_c23-Apr-12 3:33 
BugConnection line can not be aligned when nodes is too many Pinmembermaxmas22-Feb-12 22:14 
GeneralMy vote of 5 PinmemberMember 786771220-Jun-11 1:55 
Questionhorizontal tree with javascript PinmemberBadari Narayana12-Jan-11 19:32 
GeneralRe: horizontal tree with javascript PinmemberBassam Abdul-Baki18-Jan-11 4:04 
GeneralList items instead of tables Pinmemberanand sueman14-Dec-10 2:15 
GeneralFamily Tree PinmemberBahraini016-Apr-10 0:44 
QuestionHow to generate dynamic node from the database in horizontal tree view. please help me Pinmemberritesh suhane1-Mar-10 20:20 
QuestionHowto dynamicly add a node to the tree Pinmemberhacking_mike27-Aug-09 2:24 
AnswerRe: Howto dynamicly add a node to the tree Pinmemberhacking_mike28-Aug-09 5:48 
GeneralPlease Help Pinmemberluo_fen_fang21-May-09 20:16 
GeneralHuge Data Problem. Pinmemberhymns27-Apr-09 6:24 
Generalthanks very much Pinmember21cn_zy24-Jun-08 17:38 
GeneralGreat work, but look this Pinmemberzenhaust27-Apr-08 13:47 
GeneralVertical Menu limit PinmemberBoon8230-Dec-07 19:33 
GeneralRe: Vertical Menu limit Pinmembernina_lyy15-May-08 18:22 
GeneralRe: Vertical Menu limit Pinmembergirishoak4-Jan-09 19:44 
GeneralRe: Vertical Menu limit Pinmembersuperb13-May-11 0:14 
Generalgreat work Pinmemberadi_99924-Sep-07 2:33 
Questionfunctioning of the code Pinmemberseralan27-Jul-07 5:53 
AnswerRe: functioning of the code Pinmemberadi_9994-Oct-07 22:25 
QuestionPrint Problem Pinmembershelal16-Aug-06 23:45 
AnswerRe: Print Problem Pinmemberharidip14-Jun-09 20:29 

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.

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