Javascript – Traversing the HTML DOM recursively

This post will give idea about HTML DOM Objects and way we can use its methods and attributes to navigate or traverse through it.

Introduction:
How to traverse the complete HTML BODY covering each HTML element recursively and forming a tree You need to be aware of very basic Javscript DOM methods. W3schools is the wonderful resource to learn this basics. Go through all the functions but what we need for this exercise is getElementById() and getElementsByTagName()

Eg: HTML

<html>
  <head>
    <title>
      Javascript - HTML Tree Parser
    </title>
  </head>
  <body>
    <div id="header">
      <h1>Javascript - HTML Tree Parser</h1>
    </div>
    <ul id="nav">
      <li><a href="one.html">one</a></li>
      <li><a href="two.html">two</a></li>
    </ul>
    <div id="footer">
    </div>
  </body>
</html>

Step I:
We are going to write a javascript function – lets call it htmlTree

<script type="text/javascript">
   function htmlTree(){}
</script>

Step II:
We will need to first get the body element, we can do it with the help of getElementsByTagName(‘body’), which will return you the body tag. But since the function itself is plural it will always return you an array even if it has only one single element. Since body is the present only once we will refer to the 0th element of an array.

<script type="text/javascript">
  function htmlTree(){
    var body = document.getElementsByTagName('body')[0];
  }
</script>

Step III:
Now we will check if the body tag has any children – we can do that by using hasChildNodes() method, if yes then we will first refer to the first child, we can do that with the attribute “firstChild”. Also we can get the name of the tag with an attribute “tagName”. After reading the first child we will go to the next child, with use of an attribute “nextSibling”

<script type="text/javascript">
  function htmlTree(){
    var body = document.getElementsByTagName('body')[0];
    if (body.hasChildNodes()) {
      var child = body.firstChild;
      alert(child.tagName); // as per the above HTML example it will alert "div"
      var next = child.nextSibling;
      alert(next.tagName); // as per the above HTML example it will alert "ul"
    }
  }
</script>

But this wont meet our purpose, we dont know the length of the tree and we also dont know how deep each branch is going to be, so we will make this function recursive to perform these similar step for eavery branch till it reaches it leaves.

Step IV:
We will print the name of the tag identified and check if it has children if yes then we will recursively call the function again which will print its name and check if it has child – it will happen till it reaches element with no child

<script type="text/javascript">
  function htmlTree(obj){
    var obj = obj || document.getElementsByTagName('body')[0];
    alert(obj.tagName);
    if (obj.hasChildNodes()) {
      var child = obj.firstChild;
      htmlTree(child);
    }
  }
</script>

When you execute this, you get first alert as BODY but very next alert as undefined – where in it should have been DIV. This is because between BODY and the DIV tag there is the white space which is considered to be an empty text – well we need to avoid such occurrences

Step V:
Go through nodeType on W3schools. Since here we are looking for an HTML elements, we will check for nodeType = 1. If it is 1 then we will recursively call the function if not we will move onto its sibling

<script type="text/javascript">
  function htmlTree(obj){
    var obj = obj || document.getElementsByTagName('body')[0];
    alert(obj.tagName);
    if (obj.hasChildNodes()) {
      var child = obj.firstChild;
      while(child){
        if (child.nodeType === 1) {
          htmlTree(child);
        }
        child = child.nextSibling;
      }
    }
  }
</script>

Step VI:
These alerts are quiet annoying, so lets make some subtle changes so that this function returns us complete tree. I personally like “ul li ul li” to represent a tree. Here is the modified function

<script type="text/javascript">
  function htmlTree(obj){
    var obj = obj || document.getElementsByTagName('body')[0];
    var str = "<ul><li>" + obj.tagName;
    if (obj.hasChildNodes()) {
      var child = obj.firstChild;
      while (child) {
        if (child.nodeType === 1) {
          str += htmlTree(child)
        }
        child = child.nextSibling;
      }
    }
    str += "</li></ul>";
    return str;
  }
  document.write(htmlTree());
</script>

Step VII:
Since at the very beginning we were talking about just HTML elements, we will get rid of all the javascript inside HTML. We can achieve that by simply adding a simple check in the condition where we check the nodeType.

// Change
if (child.nodeType === 1)
// to
if (child.nodeType === 1 && child.nodeName != 'SCRIPT')

Demo:
Javascript – HTML Tree Parser

Word Clock dummy – Javascript

Introduction:

This tutorial will guide you to learn some basic HTML DOM Manipulation with javascript.
On the way we will try to create something similar to Word clock, although we wont be any nearer to Simon Heys’ imagination and creativity, we will try to learn somethings which is our primary goal.

We will be using javascript and CSS to achieve this.
Tutorial is aimed at javascript beginners.

You need to be knowing basics of javascript, and a bit of DOM funcionality too.
If you are new to it, dont worry we will try to learn from couple of references mentioned at appropriate places.
So just make sure you read those references, whenever you have doubt or two about whats going on.

  1. You can learn more about HTML DOM from HTML DOM Section on w3schools.
  2. And then check out the HTML DOM example section and go through the example of getElementById().
  3. Also check out the 3 useful javascript functions on web development blog of James Padolsey. We will be using those functions in our learning.

Thanx James for sharing some nice snippets. And yeh James I am using your style switcher on this site, I dont want to get heavily influenced by jQuery before learning javascript on my own and hence loved the non-jQuery version of yours. Thanx again for it.

Approach

Before starting, once again check out Word Clock.

There are certain steps we will define before starting to do anything. We will sort down the process in simple words and follow them to achieve our goal.

  1. We need to have words of all the digits that can be counted in 24 hour clock, and hence we write it down from 1-60 in words in an array.
    We are keeping it in an array so that we need not write down each html element for hours, minutes and seconds.
    When actually all of them are same [in the form of just numbers and never exceed 60].
    Also if someone wants to write a word clock for their own language then they just need to change an array.
  2. Once we get all the things in an array, we loop throug it for hours (24), minutes(60) and seconds(60) and create the elements dynamically.
  3. Once eveything created we write a recursive function, with the use of setTimeOut function, which is executed every seconds and checks the system time, and updates the particular time elements with CSS.

Example:

Here is the final result, check out the live demo over here.

Following is the CSS to be used.

body {
  background: #000;
  font-family: Verdana;
  font-size: 120%;
}

div {
  float: left;
  padding: 0 5px;
  margin: 0;
}

.hl {
  color: red;
  text-decoration: underline;
}

.norm {
  color: #454545;
}

And here is the complete javascript code. It seems quiet big but dont be afraid. We will try to understand it step by step later.

var clock = {
    units: {
        1: 'one',
        2: 'two',
        3: 'three',
        4: 'four',
        5: 'five',
        6: 'six',
        7: 'seven',
        8: 'eight',
        9: 'nine',
        10: 'ten',
        11: 'eleven',
        12: 'twelve',
        13: 'thirteen',
        14: 'fourteen',
        15: 'fifteen',
        16: 'sixteen',
        17: 'seventeen',
        18: 'eighteen',
        19: 'nineteen',
        20: 'twenty',
        21: 'twenty-one',
        22: 'twenty-two',
        23: 'twenty-three',
        24: 'twenty-four',
        25: 'twenty-five',
        26: 'twenty-six',
        27: 'twenty-seven',
        28: 'twenty-eight',
        29: 'twenty-nine',
        30: 'thirty',
        31: 'thirty-one',
        32: 'thirty-two',
        33: 'thirty-three',
        34: 'thirty-four',
        35: 'thirty-five',
        36: 'thirty-six',
        37: 'thirty-seven',
        38: 'thirty-eight',
        39: 'thirty-nine',
        40: 'forty',
        41: 'forty-one',
        42: 'forty-two',
        43: 'forty-three',
        44: 'forty-four',
        45: 'forty-five',
        46: 'forty-six',
        47: 'forty-seven',
        48: 'forty-eight',
        49: 'forty-nine',
        50: 'fifty',
        51: 'fifty-one',
        52: 'fifty-two',
        53: 'fifty-three',
        54: 'fifty-four',
        55: 'fifty-five',
        56: 'fifty-six',
        57: 'fifty-seven',
        58: 'fifty-eight',
        59: 'fifty-nine'
    },
    hour_num: null,
    min_num: null,
    sec_num: null,
    newElement: function(elementName, text, attributes){
        if (typeof elementName != "string" || typeof attributes != "object") {
            return;
        }
        var element = document.createElement(elementName);
        if (text) {
            element.appendChild(document.createTextNode(text));
        }
        for (var i in attributes) {
            element[i] = attributes[i];
        }
        return element;
    },
    toggleStyle: function(elementReference){
        var e = document.getElementById(elementReference);
        if (!e || typeof e != "object")
            return;
        e.className = (e.className === 'hl') ? 'norm' : 'hl';
    },
    writeHours: function(time_el){
        for (i = 0; i < 24; i++) {
            hour = i + 1;
            var hour_p = clock.newElement('div', clock.units[hour], {
                id: 'h' + hour,
                className: 'norm'
            });
            time_el.appendChild(hour_p);
        }
        var hour_p = clock.newElement('div', 'hours,', {
            className: 'hl'
        });
        time_el.appendChild(hour_p);
        clock.writeMinutes(time_el);

    },
    writeMinutes: function(time_el){
        for (i = 0; i < 59; i++) {
            minute = i + 1;
            var minute_p = clock.newElement('div', clock.units[minute], {
                id: 'm' + minute,
                className: 'norm'
            });
            time_el.appendChild(minute_p);
        }
        var minute_p = clock.newElement('div', 'minutes,', {
            className: 'hl'
        });
        time_el.appendChild(minute_p);
        clock.writeSeconds(time_el);
    },
    writeSeconds: function(time_el){
        for (i = 0; i < 59; i++) {
            second = i + 1;
            var second_p = clock.newElement('div', clock.units[second], {
                id: 's' + second,
                className: 'norm'
            });
            time_el.appendChild(second_p);
        }
        var second_p = clock.newElement('div', 'seconds.', {
            className: 'hl'
        });
        time_el.appendChild(second_p);
        clock.startClock();
    },
    startClock: function(){
        var initT = new Date();
        clock.hour_num = initT.getHours();
        clock.min_num = initT.getMinutes();
        clock.sec_num = initT.getSeconds();
        clock.toggleStyle('h' + clock.hour_num);
        clock.toggleStyle('m' + clock.min_num);
        clock.toggleStyle('s' + clock.sec_num);
        function updateTime(){
            var moment = new Date();
            if (clock.hour_num != moment.getHours()) {
                clock.toggleStyle('h' + clock.hour_num);
                clock.toggleStyle('h' + moment.getHours());
                clock.hour_num = moment.getHours();
            }
            if (clock.min_num != moment.getMinutes()) {
                clock.toggleStyle('m' + clock.min_num);
                clock.toggleStyle('m' + moment.getMinutes());
                clock.min_num = moment.getMinutes();
            }
            if (clock.sec_num != moment.getSeconds()) {
                clock.toggleStyle('s' + clock.sec_num);
                clock.toggleStyle('s' + moment.getSeconds());
                clock.sec_num = moment.getSeconds();
            }
            setTimeout(function(){
                updateTime();
            }, 999)
        }
        updateTime();

    },
    init: function(){
        var time_el = document.getElementById('time');
        clock.writeHours(time_el);
    }
};
window.onload = function(){
    clock.init();
};

We create a ‘clock’ object and define other details into it. We initialise it only after window is loaded. In the init function, we take a id of an element into which we are going to create elements.

var clock = {
	init: function(){
		// 'time' is an id of an element under which you are going to
		// have the word clock
        var time_el = document.getElementById('time');
		// we pass the element so that new element can
		// be added to it
        clock.writeHours(time_el);
    }
};

Step 1: Create an list of number in words from 1-60

First thing we create an object ‘unit’ with the number in words.

unit:{
	1: 'one',
	2: 'two',
		...
	59: 'fifty-nine'
}

Step 2: Create elements dynamically by looping for hours minutes and seconds.

Now we write function each to write elements in DOM, with the name writeHours, writeMinutes and writeSeconds. All 3 functions are similar

writeHours: function(time_el){
	// looping from 1 - 24 to create 24 elements
	// for hours
    for (i = 0; i < 24; i++) {
        hour = i + 1;
		// using james function to create new div elements and
		// add text intto it and attach CSS class to it
        var hour_p = clock.newElement('div', clock.units[hour], {
            id: 'h' + hour,
            className: 'norm'
        });
		// we append all the elements to time_el
        time_el.appendChild(hour_p);
    }
	// after writing all the hours we write a text 'hours' into DOM
    var hour_p = clock.newElement('div', 'hours,', {
        className: 'hl'
    });
    time_el.appendChild(hour_p);
	// after hours has been create, we move on minute function
    clock.writeMinutes(time_el);
}
writeMinutes: function(time_el){
	...
}
writeSeconds: function(time_el){
	...
}

Step 3: We start the clock and loop it indefinitely.

We call the startClock function first which sets the clock.
It takes initial values (i.e. values at that very moment) hours, minutes and seconds into hour_num, min_num, sec_num variables respectively.
These variables to cross check whether the time has updated the values of hour, minutes or seconds.

Once this is done we pass on the control to the updateTime function which runs once for every second.
While in execution it reads the values and check with the value initiated by startClock function, if number has changed then sets the former number in normal CSS and highlight the new number.

startClock: function(){
    var initT = new Date();
    clock.hour_num = initT.getHours();
    clock.min_num = initT.getMinutes();
    clock.sec_num = initT.getSeconds();
    clock.toggleStyle('h' + clock.hour_num);
    clock.toggleStyle('m' + clock.min_num);
    clock.toggleStyle('s' + clock.sec_num);
    function updateTime(){
        var moment = new Date();
        if (clock.hour_num != moment.getHours()) {
            clock.toggleStyle('h' + clock.hour_num);
            clock.toggleStyle('h' + moment.getHours());
            clock.hour_num = moment.getHours();
        }
        if (clock.min_num != moment.getMinutes()) {
            clock.toggleStyle('m' + clock.min_num);
            clock.toggleStyle('m' + moment.getMinutes());
            clock.min_num = moment.getMinutes();
        }
        if (clock.sec_num != moment.getSeconds()) {
            clock.toggleStyle('s' + clock.sec_num);
            clock.toggleStyle('s' + moment.getSeconds());
            clock.sec_num = moment.getSeconds();
        }
        setTimeout(function(){
            updateTime();
        }, 999)
    }
    updateTime();

}

Conclusion:

It might look quiet a long code and even boring explanation, but unless you all dont tell me whether I’m doing good or bad I wont be able to improve.
Anyways, I am too on a learning stages of javascript world, hence in future the posts will be much better and more efficiently and well coded. Till then experiment more and keep coming back for even more.
You can get the source code over here.

Current Page Link Highlighter – Javascript

Introduction:

This tutorial will guide you through the process of writing a javascript snippet which will help you in highlighting a hyperlink which directs to the page which you are currently viewing.

We will be using javascript and CSS to achieve this.

This tutorial is aimed at the beginners who are in the process of learning and exploring javascript which included me too.

To follow the tutorial you need to have a knowledge of basic javascript and also have to know a bit or two about DOM functions in javascript.

If DOM is new to you, please atleast learn about two functions viz. getElementById() and getElementsByTagName().

You can learn more about HTML DOM from HTML DOM Section on w3schools.

And then check out the HTML DOM example section and go through the example of getElementById() and getElementsByTagName().

Also check out the HTML DOM Location Object and go through its properties.

Limitation

This tutorial is applicable only to the hyperlinks which link to pages on the site and does not work with folders.

For eg:

This script will work with this kind of links <a href=”aboutus.html”>About Us<a>

But wont work with this kind of links <a href=”category/”>Category<a>

We will try to add that functionality in the near future.

Approach

There are certain steps we will define before starting to do anything.
We will sort down the process in simple words and follow them to achieve our goal.

  1. We will identify the url of the current page which is being viewed and then get the filename of the page from the url.

    For eg: The url of the current page being viewed is http://blog.swapnilsarwe.com/why-the-hell.html

    then the filename would be why-the-hell.html

  2. We will then identify the set of links in the menu, with the id of their parent element.
  3. We will check the set of links one at a time,
    by getting the page name of the url where the links directs us to and
    then compare that page with the pagename found in 1.
  4. If we find it we will apply a CSS class to the link, which has special properties.

Example:

We will take the following as an HTML code as an example.
You can check out the live demo over here. and can get the source code with example in zip format from here.

<div id="navbar">
<ul>
	<li><a href="index.html">Home</a></li>
	<li><a href="link1.html">Link 1</a></li>
	<li><a href="link2.html">Link 2</a></li>
	<li><a href="link3.html">Link 3</a></li>
	<li><a href="link4.html">Link 4</a></li>
	<li><a href="link5.html">Link 5</a></li>
</ul>
</div>

Functions used:

getPage() function takes in the url and returns a filename.

getPage: function(url){
	// reads the last 'slash' and takes text after it - as a pagename
	return url.substring(url.lastIndexOf('/') + 1);
}

Step 1: Identifying the page

We can get the url of the page being viewed by reading the pathname property of the Location Object.
For eg: If you are viewing the page http://example.com/link1.html
then, window.location.pathname will give you the same path ‘http://example.com/link1.html’ as a string.
We pass this url to getPage() and the function will return us back contact_us.html

var url = window.location.pathname;
curr_page = website.getPage(url);

Step 2: Identify the set of links

To get the set of links first we get their parent tag with its id.
We can do so by using the function getElementById()
And then we will use getElementsByTagName() function to identify all the <a> tags inside the parent tag with given id

// as per our example we are passing id of the parent div which is 'navbar'
var mainnav = document.getElementById('navbar');
// once we got the reference of the parent we can digg into it to find
// all the <a> elements into an array
var main_nav_links = mainnav.getElementsByTagName('a');

Step 3: Check the link

Now we have the current page and also all the links into an array, now we loop through an array.

for (i = 0; i < main_nav_links.length; i++) {
    // reading a href value from the current 'a' tag in the loop
    currHREF = main_nav_links[i].href;
    // getting a pagename from the obtained 'a' tags href
    currHREFPage = getPage(currHREF);
    // checks the obtained pagename from the current a tag with the curr_page
    if (curr_page == currHREFPage) {
		// add a code to apply a css over here and get out of the function,
		// with the return keyword since probability of finding the same
		// link in the menu again is very less
        return;
    }
}

Step 4: Apply a CSS class to the link

We can apply a CSS class to any element by simply assigning a className property

// this will assign a highlight class to the <a> element.
main_nav_links[i].className = 'highlight';

Complete JS Code

We will execute the code only when the complete window is loaded. hence we will make use of window.onload function

var website = {
    // curr_page variable initialized as '',
    // but on the object initialization
    // will take the value of the current pages url pagename
    curr_page: '',
    // initilization function
    init: function(){
        // reads the current page url
        var url = window.location.pathname;
        // assigns a filename of the url to the curr_page variable
        website.curr_page = website.getPage(url);
    },
    // reads the url and returns the name of the page from the url
    getPage: function(url){
        // reads the last 'slash' / and takes text after it as a pagename
        return url.substring(url.lastIndexOf('/') + 1);
    },
    // this function will crosscheck between the menu links and
    // the current page and highlight the link in the menu
    highlightLink: function(navlist, highlight){
        var link_array = new Array();
        // gets an reference of a object with the help of an id,
        // which will help in reading all the links under it.
        var mainnav = document.getElementById(navlist);
        // reading all the the 'a' link tags
        // falling under the object identified by the passed id.
        // we get result as a collection of all 'a' object in an array
        var main_nav_links = mainnav.getElementsByTagName('a');
        // looping through collection of 'a'
        for (i = 0; i < main_nav_links.length; i++) {
            // reading a href value from the current 'a' tag in the loop
            currHREF = main_nav_links[i].href;
            currHREFPage = website.getPage(currHREF);
            // checks the obtained clean pagename with the curr_page
            if (website.curr_page == currHREFPage) {
                // if found apply the class name,
                // which you want to apply to the specific menu item
                main_nav_links[i].className = highlight;
                // exit the function immediately
                // without checking further liks in menu
                // since to get the same link in the menu again
                // has very less probability
                // still exceptions are possible,
                // hence you can comment or remove
                // the return statement below
                // to check for each and every link
                return;
            }
        }
    }
};
// When window is loaded completely.
window.onload = function(){
    // initiate the website object
    website.init();
    // call the disableLink function,
    // param 1: id of the menu on which you want to highlight link
    // param 2: css class name which defines highlight property
    website.highlightLink('navbar', 'highlight');

};

Conclusion:

The code is ready, try it.

This will work for only those pages which has simple link format.

There are lots of new additions can be made:

  • like removing GET arguments from the links.
  • Also we can add the condition where if the page is not found,
    it is considered to be the index.html or index.php page
  • And with some additional logic we can also check for slash ‘/’ ending links and highlight them too.

Anyways will look forward to add these things soon and share it with you.