Word Clock dummy - Javascript

This tutorial is meant to learn some of the basic javascript functionalities, and while learning we will try to create something similar to Simon Heys' Word Clock.

Categories: programming, tutorials
Posted by Swapnil Sarwe on Nov 15, 2008

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.