0

I am trying to set a timer for users taking test.

The timer get a value from PHP and the jquery function is meant to initiate a timer countdown until the set time elapses.

On submission of the test form:

the function checks whether the time has elapsed, if YES, it submits all questions and whatever answered available. else if NO, it checks to see if user attempted all questions and then trigger a prompt box to notify user about any question yet to be attempted before finally submitting the test form.

So far, the time set for the test is being displayed but countdown is not working i.e the time is not counting down. Not sure but could be from the setTimeout().

Here is a jsfiddle link

jsfiddle

Below is the jquery:

$(document).ready(function(){

    var mins = <?php echo $rw_test['duration']; ?>;
    var secs = mins * 60;
    var currentSeconds = 0;
    var currentMinutes = 0;
    Decrement = function(){

        currentMinutes = Math.floor(secs / 60);
        currentSeconds = secs % 60;
        if(currentSeconds <= 9) currentSeconds = "0" + currentSeconds;
        secs--;
        var htmlVar = currentMinutes + ":" + currentSeconds;
        $('#timerText').html(htmlVar);
        if(secs !== -1) setTimeout(function(){},1000); 
        if(currentSeconds == 00 && currentMinutes==0){

/************** If User time has elapse: Submit form  ****************/

                    document.getElementById('myquiz').submit();                 
                    //document.myquiz.submit();

/************** If User time has elapse: Submit form  ****************/

        }
        else{

/************** User time still yet to elapse and form was submitted by user: check if all questions were answered  ****************/

    $('#myquiz').submit(function(){

        var errors = [];

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

            var answerCont = $(this).next();
            var inputs = $(answerCont).find('input[type=radio], input[type=checkbox]');

            if(inputs.length > 0)
            {
                var groupChecked = false;

                $(inputs).each(function(){
                    if($(this).is(':checked'))
                    {
                        groupChecked = true;
                    }
                });

                if(!groupChecked)
                {
                    errors.push($(this).text().trim());
                }
            }
        });

        if(errors.length > 0)
        {
            var errorMessage = "You forgot to attempt " + errors.length + " questions: \n\n";

            for(var i=0; i<errors.length; i++)
            {
                errorMessage += errors[i] + "\n";
            }
            //alert(errorMessage);
            //return false;
            return confirm(errorMessage+' \n\n Do you want to continue by ending the test?');
        }
    });

/************** User time still yet to elapse and form was submitted by user: check if all questions were answered  ****************/

        }

        };

        setTimeout(Decrement(),1000);   // Initiate timer and begin to Decrement user time till time elapse 

    });

Here is the html:

<div><span id="timerText"></span> (Remaining test time)</div>

Would be pleased getting some help with this.

Thanks.

John
  • 153
  • 1
  • 3
  • 13

2 Answers2

1

Let me preface this by saying that you should not rely on JavaScript timing for anything that's super time sensitive. For one, it's not accurate. Even though you may tell it to run every second it will only run as close to every second as it can; kinda whenever it gets around to it in that general time frame. This is due to the nature of JavaScript and I'll leave it to you to study more about it. Secondly, it can easily be stopped by a savvy user and then they'll be able to take the test as long as they want. You should make sure the backend code also checks how long they were taking the test (by checking the time elapsed between serving the page and processing their submission).

Ok, that said you can definitely use this to help the user have a better experience. So here we go. Small lesson in the nature of JavaScript.

What you were attempting seems to assume a procedural style of programming, which is what you'd expect in most languages. Your instructions are carried out as you wrote them, line by line. This is true with JavaScript to some degree but is not how you should think. With JS you should think in a functional style.

This means that your code should all be nicely encapsulated in functions that are called by one another as they process events and complete actions. setInterval does this. It checks the time elapsed on a loop for you and then when it sees that it's at the time you specified (or past it) it will call your function.

I've taken some liberties with your code and cleaned up a bunch and standardised the format. I found your timer code confusing so I almost entirely replaced that with something a bit simpler (simpler is almost always better):

// Config
var mins = 5; // Min test time
var secs = 0; // Seconds (In addition to min) test time
var timerDisplay = $('#timerText');

//Globals: 
var timeExpired = false;

// Test time in seconds
var totalTime = secs + (mins * 60);

// This sourced from: http://stackoverflow.com/questions/532553/javascript-countdown
var countDown = function (callback) {
    var interval;
    interval = setInterval(function () {
        if (secs === 0) {
            if (mins === 0) {
                timerDisplay.text('0:00');
                clearInterval(interval);
                callback();
                return;
            } else {
                mins--;
                secs = 60;
            }
        }
        var minute_text;
        if (mins > 0) {
            minute_text = mins;
        } else {
            minute_text = '0';
        }
        var second_text = secs < 10 ? ('0' + secs) : secs;

        timerDisplay.text(minute_text + ':' + second_text);
        secs--;
    }, 1000, timeUp);
};

You'll see that I've also added a callback to this function which is called when the timer expires. If this callback is called before the user submits, then the form should be submitted regardless of it's state of completion. If it's submitted before hand then you know the user submitted it. You can see this in practice in this fiddle. (I included the bootstrap css for prettyness)

I also corrected much of your HTML. Have a look and see what you can learn. Also, you don't need the id's on everything unless you're going to do something to them with JS. Even then it's better to use classes.

Hope it helps you out!

OneHoopyFrood
  • 3,829
  • 3
  • 24
  • 39
  • Very nice and pretty good clean up of the whole code. It works great. Thanks a bunch! – John Aug 22 '14 at 13:06
0

Yes it is not setTimeout, because it will only execute your code once after the given time. Use setInterval instead, which will execute the given code every time seconds:

setInterval(Decrement, 1000);

Also, the next time you use setTimeout or setInterval with a function that is already defined somewhere, just pass in the name of the function (like above) and not a call to the function (Decrement()). setTimeout or setInterval will take care of calling that function.

Arnelle Balane
  • 5,437
  • 1
  • 26
  • 32