Click here to Skip to main content
15,883,901 members
Articles / Programming Languages / Javascript
Article

Current Tree-Node Opener

Rate me:
Please Sign up or sign in to vote.
4.25/5 (9 votes)
23 May 20035 min read 72.2K   1.3K   26  
Simple DOM exploiting code to display the currently selected node in a HTML list tree

Introduction

This article describes a simple method of using JavaScript and the DOM to display the selected node in a HTML tree list. Most methods rely on the tree constructor to "follow the path" down the node tree to the selected item, setting each parent node to a visible mode.

This method however does the reverse; The current node is tagged and the path from it up to the root node is followed making each parent visible on the way. This method is more efficient as no searches or iterations on node collections are needed to find the next node to make visible.

This is not an article on how to make HTML tree lists, I assume you already know how to do this. You can of course view the sample code which includes HTML tree lists. That topic has been covered very well in other articles here on Code Project.

Requirements

Knowledge of HTML, CSS and JavaScript are essential. Knowledge of the Document Object Model (DOM) will help you as well.

No server-side environments, ASP/PHP/etc., are required to run the examples or use this  method. It is all client-side.

The Problem

You have this...
Sample screenshot
and you want this on-load of your page:
Sample screenshot

What you have here is a HTML tree list coded like this:

<ul id="menu">
    <li><a href="">Section 1</a></li>
    <li><a href="">Section 2</a>
        <ul>
            <li><a href="">Section 2.1</a></li>
            <li><a href="">Section 2.2</a></li>
            <li><a href="">Section 2.3</a>
                <ul>
                    <li><a href="">Section 2.3.1</a></li>
                    <li><a href="">Section 2.3.2</a></li>
                </ul>
            </li>
            <li><a href="">Section 2.4</a></li>
        </ul>
    </li>
    <li><a href="">Section 3</a></li>
</ul>

Each UL within a LI will have it's display style attribute set to none in a CSS file. e.g. ul#menu li ul { display:none; }. This is a great setup allowing simple JavaScript to open and close nodes as the user needs.

However when it comes to loading a page and having a sub-node displayed in a visible/open mode by default this structure makes it less than easy. You cannot very well expect users to click back down through the tree structure every time they visit a new page. The tree structure should remain open to the node they clicked on.

The conventional way of sorting this problem out is with code like so:

<ul id="menu">
    <li><a href="">Section 1</a></li>
    <li><a href="">Section 2</a>
        <ul class="open">
            <li><a href="">Section 2.1</a></li>
            <li><a href="">Section 2.2</a></li>
            <li><a href="">Section 2.3</a>
                <ul class="open">
                    <li><a href="">Section 2.3.1</a></li>
                    <li><a href="">Section 2.3.2</a></li>
                </ul>
            </li>
            <li><a href="">Section 2.4</a></li>
        </ul>
    </li>
    <li><a href="">Section 3</a></li>
</ul>

Each parent of the node you want to display needs an assigned class of open. In your CSS you then have ul#menu li ul.open { display: block }.

It works but the main problem is that you have to concoct code which assigns that class to each node that needs it. Not only is the code for this not easy but it is also inefficient. Another really big problem is that you cannot easily cache your tree code as with each navigation event the code changes. It basically becomes per-user code, rendering caching a bit pointless. There are ways around that but the method I will explain below needs none of this.

Looking Up, Another Way

The DOM element property of parentElement is the key. Basically ever element excluding HTML must have a parent element. The tree view code of above is especially dependant on this child/parent structure.

To kick this off here is what I propose; Walk up the tree. Start with the selected node and then using parentElement you walk up the tree to the "root" node of the menu. Everytime you hit a UL element you simply assign block to it's display style attribute. Damned simple IMO. No looping through other elements on the same level is required. You don't need to check if the node is on the right path because it automatically is, by virtue of it being a parent of the node you have walked up from.

Since you are all code-heads you will probably understand the code better than that explanation:

function showCurrentSection()
{        
    var objCurrentSection = document.getElementById("navcurrentsection");        
    if (objCurrentSection != null)
    {
        objCurrentSection.style.display = "block";            
        objCurrentSection.parentElement.childNodes[0].className = "open";
        if (objCurrentSection.parentElement.parentElement.nodeName == "UL")
            showSection(objCurrentSection.parentElement.parentElement);        
    }
}

function showSection(objSection)
{    
    objSection.style.display = "block";
    objSection.parentElement.childNodes[0].className = "open";    
    if (objSection.parentElement.parentElement != null && objSection.parentElement.parentElement.nodeName == "UL")
        showSection(objSection.parentElement.parentElement);
}

You have two functions. showCurrentSection is kicked off normally by an onload event e.g. <body onload="showCurrentSection();">. showCurrentSection calls showSection which calls itself until no more valid parent elements are found.

showCurrentSection starts with the line document.getElementById("navcurrentsection"). There is one modification to the HTML tree list code you need to make:

<ul id="menu">
    <li><a href="">Section 1</a></li>
    <li><a href="">Section 2</a>
        <ul>
            <li><a href="">Section 2.1</a></li>
            <li><a href="">Section 2.2</a></li>
            <li><a href="">Section 2.3</a>
                <ul id="navcurrentsection" >
                    <li><a href="">Section 2.3.1</a></li>
                    <li><a href="">Section 2.3.2</a></li>
                </ul>
            </li>
            <li><a href="">Section 2.4</a></li>
        </ul>
    </li>
    <li><a href="">Section 3</a></li>
</ul>

You need to tag the section so that the walker can find where it should start from. document.getElementById("navcurrentsection") returns an object reference to whatever UL element has that specific ID.

The if (objCurrentSection != null) is just there to ensure that indeed a section is currently selected. For instance on the home page of your website probably no node will be selected, in which case the walker should not run.

Next we just set the navcurrentsection node to be displayed. We then change the classname of the node so that any visualisation of an open node are applied, e.g. an open folder graphic. Next the code checks wether the element two levels up is a UL. Remember that a sub node is a UL contained within a LI element i.e. The parent is actually a LI not a UL. You don't want to touch the LI, so two levels up you go.

If it is a UL then we call the showSection function and pass that parent-parent element to it.

showSection does the actual walking as it calls itself until objSection.parentElement.parentElement != null && objSection.parentElement.parentElement.nodeName == "UL" is false. i.e. Until no more parent-parent elements correspond to a UL element. nodeName returns the name of the element. e.g. A for <a href=""></a>, DIV for <div></div>, UL for <UL></UL> etc.

This obviously means that your HTML tree list code needs to be well coded, valid in otherwords.

Conclusion

All in all quite simple really. Certainly improvements can be made in the JavaScript code, but the idea is there and it works quite well. The main thing I like about this method is that you just output your HTML tree list code, bang in a tag and run the script. You do not have to change the code which generates your HTML tree list. It also gives a taste of the power of the DOM.

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


Written By
Web Developer Caliber AI
South Africa South Africa
My name is Paul Watson and I have been a professional web-developer since 1997. I live and work remotely from Cape Town, South Africa.

I have many years of experience with HTML, CSS, JavaScript, PostgreSQL, and Ruby on Rails. I am capable in Python and Machine Learning too.

Currently I am the CTO of CaliberAI. Formerly I worked with Kinzen (CTO & co-founder), Storyful (CTO, acquired by News Corp), FeedHenry (co-founder, acquired by Red Hat), and ChangeX.

Now that you know a bit about me why not say hello.

Comments and Discussions

 
-- There are no messages in this forum --