Home » 2011 » November

Using jQuery to Select the Last Node in a Tree

I worked on some code to show the classes that were being used on some div’s for a presentation. I wanted to be able to have the text of the class attribute show in the final container to give an idea of how the layout ended up looking and why it looked that way.

This is basically a problem of finding all the last nodes in every branch of a tree. The div’s in my case were not all the same depth from the top container

<div id="top">
    <div>
        <div></div>
        <div>
            <div></div>
            <div></div>
            <div></div>
        </div>
    </div>
    <div>
        <div>
            <div></div>
            <div></div>
        </div>
        <div>
            <div>
                <div></div>
                <div></div>
            </div>
            <div></div>
        </div>
        <div></div>
    </div>
</div>

The way the code works is you recursively call a function that looks at all the children nodes of a node. Then you pass the children to the same function. You check to see if the node you are on has no more children and when that’s true you know you have reached the bottom of a branch in your markup tree. In my example I was using jQuery.children() but the base DOM has a similar ChildNodes function.

function ShowLowestDivStyle(parent) {
    var children = parent.children();

    if (children.length > 0) {
        children.each(function (index) {
            ShowLowestDivStyle($(children[index]));
        });
    }
    else {
        //This branch lets us use the last node
        parent.text(parent.attr('class'));
    }
}

However afterward I wanted to see if I could do the same thing with a closed from solution. My idea is to create a loop where each time we take a list of current nodes and check if they have children. If not we perform some sort of action on them. Else we get rid of our current node list and then promote our list of children to current nodes. This loop would walk down a tree one level of depth at a time.

function WorkingWithChildren(rootNode){
    var depth = 0;
    var currentNodes = $();
    
    currentNodes = rootNode.children('div');
    
    //Find all the end nodes in each branch
    while (currentNodes.length > 0) {
        //Get the next set of child nodes
        var nextNodes = currentNodes.children('div');
        
        //Perform actions on childless nodes
        currentNodes.each(function () {
            //Convert DOM node to jQuery node
            var jNode = $(this);
            if(jNode.children('div').length == 0){
                jNode.text(depth);
                jNode.attr('style', 'margin-left:' + depth + '0px;');
            }
        });
        
        //Update the list of current nodes
        currentNodes = nextNodes;
        depth++;
    }
}

If you run the script you should see each div gets indented by its level of depth and contains its level in the tree.

Looking at JavaScript Hash as a Case Alternitive

I had to do a mapping today and used the hash properties of objects in JavaScript to take care of it.

function a(key) {
    var myHash = {
        'valueOne':'mapedToA',
        'valueTwo':'mapedToB',
        'valueThree':'mapedToC',
    };

    return myHash[key];
}

Later on someone asked me about a similar issue they were trying to solve in their own code and I noticed they were using a case statement for the same type of thing.

function b(key) {
    var retVal = '';
    switch(key){
        case 'valueOne':
            retVal = 'mapedToA';
            break;
        case 'valueTwo':
            retVal =  'mapedToB';
            break;
        case 'valueThree':
            retVal =  'mapedToC';
            break;
        default:
            retVal = undefined;
            break;
    }
    
    return retVal;
}

Now, of course, I like my code better. However in this case the hash is cleaner. Seeing my hash in place of a case statement got me thinking of the switch / case pattern. In this example the hash and the case are interchangeable. So I started thinking of how in JavaScript with functions as objects, a hash could perform in the same way as the a switch / case.

Look at this case pattern:

function c(key) {
    switch(key){
        case 'valueOne':
            //do some work because it valueOne
            break;
        case 'valueTwo':
            //do some other work because it valueTwo
            break;
        default:
            //Cover our corner cases
            break;
    }
    
    return retVal;
}

Using lambdas we could do something similar with a hash:

function d(key) {
    var myHash = {
        'valueOne': function {
            //do some work because it valueOne
        },
        'valueTwo': function {
            //do some work because it valueTwo
        }
    };

    myHash[key]();
}

Now though initially when I had the idea it was quite exciting. In implementation it ends up having very similar layout and syntax to the case statement. The additional thing to notice is that when using the hash there is no default action if the value is not in the hash. When passing in a value not in our hash, an exception is thrown that the function with the name of the missing value is not defined. However it’s not much code to take care of that issue as well.

function e(key) {
    var myHash = {
        'valueOne': function() {
            //do some work because it valueOne
            alert('mapedToA');
        },
        'valueTwo': function() {
            //do some work because it valueOne
            alert('mapedToB');
        }
    };

    myHash.hasOwnProperty(key) ?
        myHash[key](): //Have key, carry on
        (function() {  //Else cover our bums
            //Cover the corner case
        })();
}

The fix is to check if the object has that property then we can execute an alternative function to cover that case. Could this have been done with an if / else instead of an ternary statement. Yeah, but the point of this exercise was to experiment with the application of functions as objects. So as soon as we define it we execute it.

With this pattern we ‘could’ use the hash in place of the case. By now we can see that the syntax of a case is much more appropriate and concise for anything beyond a simple mapping. It’s things like this that I like to explore and be creative with in JavaScript.