HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptModerate

Basic timer from one minute to 0

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
onetimerfrombasicminute

Problem

I'm a beginner in JavaScript and I am sure that there is a better way to do it (basic one minute timer from 01:00 to 00:00 running from pageload):

JSFiddle

window.onload=function(){
(function(){
     setInterval(function(){
     cro();
     document.getElementById('unminut').innerHTML="0"+jocminut+":"+jocsecunda1+jocsecunda2;
    },1000);
    }());
}
var jocminut = 1;
var jocsecunda1 = 6;
var jocsecunda2 = 10;
function cro(){
    if(jocsecunda1!=0||jocsecunda2!=0){
        jocsecunda2 -=1;
        while(jocminut==1){
            jocminut-=1;
            jocsecunda1 -=1;
        }   
            while(jocsecunda2==-1){
                jocsecunda1-=1;
                jocsecunda2=9;

            }

    }
}

Solution

You want to be careful with setInterval() and setTimeout(), because javascript runs in only a single thread if the event loop is busy it won't fire the code in setInterval() or setTimeout() exactly as expected. This means your timer could take longer than you expected.

Instead of starting with a counter and decrementing on each interval, you can calculate a time difference and then use setInterval() to refresh the frontend. This will be more accurate.

The following code calculates a completion date and then can calculate how many seconds are remaining.

//sets a completion date 60 seconds into the future
    // Date.getTime() returns the time in milliseconds
    var completion = new Date(new Date().getTime() + 60000),
    function calculateSecondsRemaining() {
        var now = new Date(),
            //Math.max is used to prevent negative numbers from being returned
            differenceInMilliseconds = Math.max(0, completion.getTime() - now.getTime()),
            differenceInSeconds = Math.floor(differenceInMilliseconds / MILLISECONDS_IN_SECOND);
        return differenceInSeconds;
    }


calculateSecondsRemaining() handles the complex logic that was once in cro(), but I feel that is is more readable. When dealing with time and numbers, tracking each digit can be tedious. I prefer to not deal with it at that level.

I prefer to split logic into multiple functions so that the code is more readable. In your code what jumped out at me is three different bits of logic, calculateSecondsRemaining(), updateDisplay(), and formatNumberTo2Places(). By spliting the functions up, the logic within your setInterval() is very small and very easy to understand.

As a final note, once your timer hits zero, the interval no longer needs to fire. The following code cancel the interval using clearInterval() once the timer has reached zero.

var refreshIntervalId = setInterval(function () {
        var secondsRemaining = calculateSecondsRemaining();
        updateDisplay(secondsRemaining);
        if (secondsRemaining <= 0) {
            //clear timer once time expired
            clearInterval(refreshIntervalId);
        }
    }, 1000);


All Together

The following is wrapped in a self-invoking anonymous function to prevent spilling code into the global scope.

(function (window, document, undefined) {
    var MILLISECONDS_IN_SECOND = 1000,
        SECONDS_IN_MINUTE = 60,
        timerLength = 60, //seconds
        start = new Date(),
        completion = new Date(start.getTime() + (timerLength * MILLISECONDS_IN_SECOND)),
        pentruminut = document.createElement('span');

    // add elements
    document.body.appendChild(pentruminut);

    function formatNumberTo2Places(n) {
        //this assumes 1 or 2 digit positive numbers
        var buffer = (n > 9) ? "" : "0";
        return buffer + n;
    }

    function calculateSecondsRemaining() {
        var now = new Date(),
            //Math.max is used to prevent negative numbers from being returned
            differenceInMilliseconds = Math.max(0, completion.getTime() - now.getTime()),
            differenceInSeconds = Math.floor(differenceInMilliseconds / MILLISECONDS_IN_SECOND);
        return differenceInSeconds;
    }

    function updateDisplay(secondsRemaining) {
        var minutes = Math.floor(secondsRemaining / SECONDS_IN_MINUTE),
            seconds = Math.floor(secondsRemaining % SECONDS_IN_MINUTE);
        pentruminut.innerHTML = formatNumberTo2Places(minutes) + ":" + formatNumberTo2Places(seconds);
    }

    window.onload = function () {
        var refreshIntervalId = setInterval(function () {
            var secondsRemaining = calculateSecondsRemaining();
            updateDisplay(secondsRemaining);
            if (secondsRemaining <= 0) {
                //clear timer once time expired
                clearInterval(refreshIntervalId);
            }
        }, 1 * MILLISECONDS_IN_SECOND);
    };
}(window, document));

Code Snippets

//sets a completion date 60 seconds into the future
    // Date.getTime() returns the time in milliseconds
    var completion = new Date(new Date().getTime() + 60000),
    function calculateSecondsRemaining() {
        var now = new Date(),
            //Math.max is used to prevent negative numbers from being returned
            differenceInMilliseconds = Math.max(0, completion.getTime() - now.getTime()),
            differenceInSeconds = Math.floor(differenceInMilliseconds / MILLISECONDS_IN_SECOND);
        return differenceInSeconds;
    }
var refreshIntervalId = setInterval(function () {
        var secondsRemaining = calculateSecondsRemaining();
        updateDisplay(secondsRemaining);
        if (secondsRemaining <= 0) {
            //clear timer once time expired
            clearInterval(refreshIntervalId);
        }
    }, 1000);
(function (window, document, undefined) {
    var MILLISECONDS_IN_SECOND = 1000,
        SECONDS_IN_MINUTE = 60,
        timerLength = 60, //seconds
        start = new Date(),
        completion = new Date(start.getTime() + (timerLength * MILLISECONDS_IN_SECOND)),
        pentruminut = document.createElement('span');

    // add elements
    document.body.appendChild(pentruminut);

    function formatNumberTo2Places(n) {
        //this assumes 1 or 2 digit positive numbers
        var buffer = (n > 9) ? "" : "0";
        return buffer + n;
    }

    function calculateSecondsRemaining() {
        var now = new Date(),
            //Math.max is used to prevent negative numbers from being returned
            differenceInMilliseconds = Math.max(0, completion.getTime() - now.getTime()),
            differenceInSeconds = Math.floor(differenceInMilliseconds / MILLISECONDS_IN_SECOND);
        return differenceInSeconds;
    }

    function updateDisplay(secondsRemaining) {
        var minutes = Math.floor(secondsRemaining / SECONDS_IN_MINUTE),
            seconds = Math.floor(secondsRemaining % SECONDS_IN_MINUTE);
        pentruminut.innerHTML = formatNumberTo2Places(minutes) + ":" + formatNumberTo2Places(seconds);
    }

    window.onload = function () {
        var refreshIntervalId = setInterval(function () {
            var secondsRemaining = calculateSecondsRemaining();
            updateDisplay(secondsRemaining);
            if (secondsRemaining <= 0) {
                //clear timer once time expired
                clearInterval(refreshIntervalId);
            }
        }, 1 * MILLISECONDS_IN_SECOND);
    };
}(window, document));

Context

StackExchange Code Review Q#56381, answer score: 12

Revisions (0)

No revisions yet.