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

jQuery stopwatch

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

Problem

Here is the link to the jsFiddle and below is the code:

JS:

```
$(function() {

var hours = minutes = seconds = milliseconds = 0;
var prev_hours = prev_minutes = prev_seconds = prev_milliseconds = undefined;
var timeUpdate;

// Start/Pause/Resume button onClick
$("#start_pause_resume").button().click(function(){
// Start button
if($(this).text() == "Start"){ // check button label
$(this).html("Pause");
updateTime(0,0,0,0);
}
// Pause button
else if($(this).text() == "Pause"){
clearInterval(timeUpdate);
$(this).html("Resume");
}
// Resume button
else if($(this).text() == "Resume"){
prev_hours = parseInt($("#hours").html());
prev_minutes = parseInt($("#minutes").html());
prev_seconds = parseInt($("#seconds").html());
prev_milliseconds = parseInt($("#milliseconds").html());

updateTime(prev_hours, prev_minutes, prev_seconds, prev_milliseconds);

$(this).html("Pause");
}
});

// Reset button onClick
$("#reset").button().click(function(){
if(timeUpdate) clearInterval(timeUpdate);
setStopwatch(0,0,0,0);
$("#start_pause_resume").html("Start");
});

// Update time in stopwatch periodically - every 25ms
function updateTime(prev_hours, prev_minutes, prev_seconds, prev_milliseconds){
var startTime = new Date(); // fetch current time

timeUpdate = setInterval(function () {
var timeElapsed = new Date().getTime() - startTime.getTime(); // calculate the time elapsed in milliseconds

// calculate hours
hours = parseInt(timeElapsed / 1000 / 60 / 60) + prev_hours;

// calculate minutes
minutes = parseInt(timeElapsed / 1000 / 60) + prev_minutes;
if (minutes > 60) minutes %= 60;

// calculate s

Solution

Chess clock anyone? :D

HTML:


    
         : 
         : 
         :: 
        
    
    
        
        Start
        Reset
    


CSS:

/*
Always prefix your styles with a unique selector your widget has
to prevent your styles from affecting other elements of the same
selector pattern. In this case, only those under .stopwatch gets
affected.
*/
.stopwatch .controls {
    font-size: 12px;
}

/* I'd rather stick to CSS rather than JS  for styling */

.stopwatch .controls button{
    padding: 5px 15px;
    background :#EEE;
    border: 3px solid #06C;
    border-radius: 5px
}

.stopwatch .time {
    font-size: 150%;
}


JS:

$(function () {

    // Never assume one widget is just used once in the page. You might
    // think of adding a second one. So, we adjust accordingly.

    $('.stopwatch').each(function () {

        // Cache very important elements, especially the ones used always
        var element = $(this);
        var running = element.data('autostart');
        var hoursElement = element.find('.hours');
        var minutesElement = element.find('.minutes');
        var secondsElement = element.find('.seconds');
        var millisecondsElement = element.find('.milliseconds');
        var toggleElement = element.find('.toggle');
        var resetElement = element.find('.reset');
        var pauseText = toggleElement.data('pausetext');
        var resumeText = toggleElement.data('resumetext');
        var startText = toggleElement.text();

        // And it's better to keep the state of time in variables 
        // than parsing them from the html.
        var hours, minutes, seconds, milliseconds, timer;

        function prependZero(time, length) {
            // Quick way to turn number to string is to prepend it with a string
            // Also, a quick way to turn floats to integers is to complement with 0
            time = '' + (time | 0);
            // And strings have length too. Prepend 0 until right.
            while (time.length < length) time = '0' + time;
            return time;
        }

        function setStopwatch(hours, minutes, seconds, milliseconds) {
            // Using text(). html() will construct HTML when it finds one, overhead.
            hoursElement.text(prependZero(hours, 2));
            minutesElement.text(prependZero(minutes, 2));
            secondsElement.text(prependZero(seconds, 2));
            millisecondsElement.text(prependZero(milliseconds, 3));
        }

        // Update time in stopwatch periodically - every 25ms
        function runTimer() {
            // Using ES5 Date.now() to get current timestamp            
            var startTime = Date.now();
            var prevHours = hours;
            var prevMinutes = minutes;
            var prevSeconds = seconds;
            var prevMilliseconds = milliseconds;

            timer = setInterval(function () {
                var timeElapsed = Date.now() - startTime;

                hours = (timeElapsed / 3600000) + prevHours;
                minutes = ((timeElapsed / 60000) + prevMinutes) % 60;
                seconds = ((timeElapsed / 1000) + prevSeconds) % 60;
                milliseconds = (timeElapsed + prevMilliseconds) % 1000;

                setStopwatch(hours, minutes, seconds, milliseconds);
            }, 25);
        }

        // Split out timer functions into functions.
        // Easier to read and write down responsibilities
        function run() {
            running = true;
            runTimer();
            toggleElement.text(pauseText);
        }

        function pause() {
            running = false;
            clearTimeout(timer);
            toggleElement.text(resumeText);
        }

        function reset() {
            running = false;
            pause();
            hours = minutes = seconds = milliseconds = 0;
            setStopwatch(hours, minutes, seconds, milliseconds);
            toggleElement.text(startText);
        }

        // And button handlers merely call out the responsibilities
        toggleElement.on('click', function () {
            (running) ? pause() : run();
        });

        resetElement.on('click', function () {
            reset();
        });

        // Another advantageous thing about factoring out functions is that
        // They are reusable, callable elsewhere.
        reset();
        if(running) run();
    });

});

Code Snippets

<!-- 
Never assume just one. Prepare for more than one always. 
With that, we use classes
-->

<div class="stopwatch" data-autostart="false">
    <div class="time">
        <span class="hours"></span> : 
        <span class="minutes"></span> : 
        <span class="seconds"></span> :: 
        <span class="milliseconds"></span>
    </div>
    <div class="controls">
        <!-- Some configurability -->
        <button class="toggle" data-pausetext="Pause" data-resumetext="Resume">Start</button>
        <button class="reset">Reset</button>
    </div>
</div>
/*
Always prefix your styles with a unique selector your widget has
to prevent your styles from affecting other elements of the same
selector pattern. In this case, only those under .stopwatch gets
affected.
*/
.stopwatch .controls {
    font-size: 12px;
}

/* I'd rather stick to CSS rather than JS  for styling */

.stopwatch .controls button{
    padding: 5px 15px;
    background :#EEE;
    border: 3px solid #06C;
    border-radius: 5px
}

.stopwatch .time {
    font-size: 150%;
}
$(function () {

    // Never assume one widget is just used once in the page. You might
    // think of adding a second one. So, we adjust accordingly.

    $('.stopwatch').each(function () {

        // Cache very important elements, especially the ones used always
        var element = $(this);
        var running = element.data('autostart');
        var hoursElement = element.find('.hours');
        var minutesElement = element.find('.minutes');
        var secondsElement = element.find('.seconds');
        var millisecondsElement = element.find('.milliseconds');
        var toggleElement = element.find('.toggle');
        var resetElement = element.find('.reset');
        var pauseText = toggleElement.data('pausetext');
        var resumeText = toggleElement.data('resumetext');
        var startText = toggleElement.text();

        // And it's better to keep the state of time in variables 
        // than parsing them from the html.
        var hours, minutes, seconds, milliseconds, timer;

        function prependZero(time, length) {
            // Quick way to turn number to string is to prepend it with a string
            // Also, a quick way to turn floats to integers is to complement with 0
            time = '' + (time | 0);
            // And strings have length too. Prepend 0 until right.
            while (time.length < length) time = '0' + time;
            return time;
        }

        function setStopwatch(hours, minutes, seconds, milliseconds) {
            // Using text(). html() will construct HTML when it finds one, overhead.
            hoursElement.text(prependZero(hours, 2));
            minutesElement.text(prependZero(minutes, 2));
            secondsElement.text(prependZero(seconds, 2));
            millisecondsElement.text(prependZero(milliseconds, 3));
        }

        // Update time in stopwatch periodically - every 25ms
        function runTimer() {
            // Using ES5 Date.now() to get current timestamp            
            var startTime = Date.now();
            var prevHours = hours;
            var prevMinutes = minutes;
            var prevSeconds = seconds;
            var prevMilliseconds = milliseconds;

            timer = setInterval(function () {
                var timeElapsed = Date.now() - startTime;

                hours = (timeElapsed / 3600000) + prevHours;
                minutes = ((timeElapsed / 60000) + prevMinutes) % 60;
                seconds = ((timeElapsed / 1000) + prevSeconds) % 60;
                milliseconds = (timeElapsed + prevMilliseconds) % 1000;

                setStopwatch(hours, minutes, seconds, milliseconds);
            }, 25);
        }

        // Split out timer functions into functions.
        // Easier to read and write down responsibilities
        function run() {
            running = true;
            runTimer();
            toggleElement.text(pauseText);
        }

        function pause() {
            running = false;
            clearTimeout(timer)

Context

StackExchange Code Review Q#48383, answer score: 8

Revisions (0)

No revisions yet.