Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create a jquery stopwatch which begins on user focus, stops on focus out and begins a new timer

I'm in the process of building a simple web app that allows a user to quiz his/herself on math problems. The user first chooses the type of problem she'd like to do and then how many she'd like to do.

As the user is going through the problems, I'd like to time how long it takes to complete each problem. This is the code that displays the problems on the page:

function displayMult ($i)
{
echo '<table class="basic">';
    for($k=0; $k < $i; ++$k) // display problems
    {
        $user_response[$k] = 'user_response' . $k ;
        echo '<tr>';
        echo '<td>' . number_format($_SESSION['mult_one'][$k]) . '</td>';
        echo '<td>'. ' x ' . '</td>';
        echo '<td>' . number_format($_SESSION['mult_two'][$k]) . '</td>';
        echo '<td>' . ' =   <input type="text" class="field" name="user_response' . $k . '"' . 'id="u_r' . $k . '" />' . '</td>';
        echo '<td id="timer_' . $k . '">test</td>';
        echo '</tr>';
    }
echo '</table>';
}

And this is the jQuery I have that times the user responses. The problem I'm having is that 1) I can't figure out a way to write to always select the correct timer ID to pass into the stopwatch function and 2) stop old timer and begin new one. The current code will only time the first question and then correctly stop the timer when the user clicks on the input field for the next question.

$(document).ready(function() {
    $('.field').focus(stopwatch('#timer_0'));
});

function stopwatch(container) {
  var interval;
  return function() {
    if (interval) {
       clearInterval(interval); 
    }
    else{
        var count = 0;
        interval = setInterval(function() {
            count++;
            $(container).html(count + ' seconds');
        }, 1000);
    }
  };
}

I'm new to jquery so any help and insight would be greatly appreciated! Thanks in advance.

like image 742
john k Avatar asked May 22 '11 20:05

john k


2 Answers

Here's my go at it

var timer_id;

$(document).ready(function() {
    $('.field').focus(function() {
        $(this).addClass('active_input');
        timer_id = setInterval('tick()', 1000);
    }).blur(function() {
        $(this).removeClass('active_input');
        clearTimeout(timer_id);
    });
});

function tick() {
    var id_n = $('.active_input').attr('id').substring(3);
    var t_td = $('#timer_' + id_n);
    var t = parseInt(t_td.text(), 10);
    if (isNaN(t)) {
        t = '1 second';
    } else {
        t++;
        t += ' seconds'
    }
    t_td.text(t);
}
like image 162
Majid Fouladpour Avatar answered Sep 23 '22 14:09

Majid Fouladpour


Part 1: Rather than returning a function in stopwatch, what you probably want to do is put an inline function in the focus() call that gets some info from the field about which timer it should be sent to. If you're using at least jQuery 1.5, you can set an attribute in the HTML that you can retrieve using the jquery .data() method.

Part 2: The problem you're running into here is the count and interval handle aren't in the right scope when you call them later. You can actually do this without intervals at all by just using .data() again to store a start time on the timer element, and then attach a handler to each field's blur event to stop it and display the result.

Here's the PHP:

function displayMult ($i)
{
echo '<table class="basic">';
    for($k=0; $k < $i; ++$k) // display problems
    {
        $user_response[$k] = 'user_response' . $k ;
        echo '<tr>';
        echo '<td>' . number_format($_SESSION['mult_one'][$k]) . '</td>';
        echo '<td>'. ' x ' . '</td>';
        echo '<td>' . number_format($_SESSION['mult_two'][$k]) . '</td>';
        echo '<td>' . ' =   <input data-timer="#timer_' . $k . '" type="text" class="field" name="user_response' . $k . '"' . 'id="u_r' . $k . '" />' . '</td>';
        echo '<td id="timer_' . $k . '">test</td>';
        echo '</tr>';
    }
echo '</table>';
}

Here's the JS:

$(document).ready(function() {
    $('.field').focus(function() {
        var timer_id = $(this).data("timer");
        stopwatch("#" + timer_id);
    });
    $('.field').blur(function() {
        var timer_id = $(this).data("timer");
        stopwatch("#" + timer_id);
    });
});

function stopwatch(container_id) {
  var $container = $(container_id);
  var time_now = new Date();
  var time_started = $container.data("time");
  if (time_started) {
    time_taken = time_now - time_started; // This is in milliseconds
    $container.html(time_taken / 1000 + ' seconds');
  }
  else {
    $container.data("time", time_now);
  }
}
like image 27
latrani Avatar answered Sep 21 '22 14:09

latrani