views:

1234

answers:

7

So I made some timers for a quiz. The thing is, I just realized when I put

javascript: alert("blah");

in the address, the popup alert box pauses my timer. Which is very unwanted in a quiz.

I don't think there is any way to stop this behaviour... but I'll ask anyway.

If there is not, mind suggesting what should I do?

+5  A: 

No, there is no way to prevent alert from stopping the single thread in JavaScript. Probably you can use some other way of user notification, for example a floating layer.

Sergey Ilinsky
doesn't answer his question, read it again
davr
Actually it does, this seems to be a two part question - 1) can I stop this behavior? 2) if not, what else can I do to display notifications?
matt b
+3  A: 

It's modal and stops execution. Consider an alternative which does not pause execution like a Lightbox technique.

micahwittman
doesn't answer his question, read it again
davr
+5  A: 

Apparently the preview rendering differs from the posted rendering. This paragraph is here to make sure the next two lines show up as code.

// Preserve native alert() if you need it for something special
window.nativeAlert = window.alert;

window.alert = function(msg) {
    // Do something with msg here. I always write mine to console.log,
    // but then I have rarely found a use for a real modal dialog,
    // and most can be handled by the browser (like window.onbeforeunload).
};
eyelidlessness
Exactly what I need. Thanks.
SyaZ
Note: While I know the alert is still accessible with javascript: nativeAlert(); but at least it require source viewing and some JS understanding rather than blindly trying alert();
SyaZ
You can also wrap nativeAlert in a closure to make it private.
eyelidlessness
This is nice, but the right answer is Filini's. Always assume that browser not under you *immediate* control are corrupt, rigged, whatever. And by "immediate control" I mean within *BEAT-WITH-STICK* distance.
Anders Eurenius
Agreed, actually. But I did want to directly answer the specific question (how to override alert).
eyelidlessness
+2  A: 

I think the question asker is trying to prevent cheating. Since a user can type javascript: alert("paused"); into the address bar, or make a bookmarklet to do that, it's easy to pause the quiz and cheat.

The only thing I can think of is to use Date() to get the current time, and check it again when the timer fires. Then if the time difference is not reasonably close to the intended timer duration, show an admonishment and disqualify the answer to that question or let them flunk the quiz. There is no way to prevent the user from pausing your quiz, but it should be possible to catch them.

Of course with any cheat-proofing, you motivate people to become better cheaters. A person could change the system time on their PC, and fool the javascript Date() constructor which gets the time from the operating system.

You can use an interval to do a repeated clock comparison against a one second interval length. The interval handler can also update a time-remaining field on the user's display. Then the users can feel the pressure build as time runs out on their quiz. Fun times!

Mnebuerquo
Thanks for your insights. Good thoughts indeed.
SyaZ
Glad you asked the question. I hadn't thought about using alert as a pause button before. Also glad you asked because the chosen answer is something I hadn't seen before. Now I can cheat on timed javascript quizzes by making myself an alert bookmarklet! (Until they read this page)
Mnebuerquo
+8  A: 

Never, ever rely on javascript (or any other client-side time) to calculate elapsed times for operations done between postbacks, or different pages.

If you always compare server dates, it will be hard for people to cheat:

  1. first page request, store the server time
  2. ping with javascript calls each N seconds, compare the 2 server times, and return the elapsed (just for show)
  3. when the user submits the form, compare the 2 server times, calculate the elapsed time, and discard the ones which took too long (ie: possible cheaters)
Filini
+2  A: 

The feedback loop on SyaZ's question has clarified the issues at stake.

Here's an attempt to summarize the good answers so far:

  • Client scripts are by nature are easy to manipulate to cheat an online quiz. SEE @Filini 's Server-side approach

  • window.alert = function(msg) {} will overriding alert() and perhaps defeat the low hanging fruit of putting in the addressbar: javascript:alert('Pausing page so I can google the answer') or I'll use my Phone-A-Friend now. Courtesy of @eyelidlessness

  • If you must use a client-side approach, instead of using setTimeOut(), you could use a custom date-compare-based pause function like this (concept by @Mnebuerquo, code example by me (@micahwittman)):

Example:

var beginDate = new Date();

function myTimeout(milsecs){
  do { curDate = new Date(); }
    while((curDate-beginDate) < milsecs);
}

function putDownYourPencils(milsecs){
  myTimeout(milsecs);
  var seconds = milsecs / 1000;
  alert('Your '  + seconds + ' seconds are up. Quiz is over.');
}

putDownYourPencils(3000);
micahwittman
+1  A: 

Ultimately, you cannot trust user input. Without keeping track of the time elapsed on the server, there's just no guarantee the data hasn't been manipulated.

However, if you're confident your quiz-takers aren't JavaScript-savvy, and are merely relying on a "trick" they found somewhere, you could test for cheating (pausing) with the following code, which doesn't require modifying window.alert:

var timer = {
    startDatetime: null,
    startSec: 0,
    variance: 1,
    exitOnPause: true,
    count: function (config) {
     var that = this;

     if (typeof config == "object" && typeof parseInt(config.seconds) == "number" && !isNaN(parseInt(config.seconds)))
     {
      if (typeof parseFloat(config.variance) == "number" && !isNaN(parseFloat(config.variance))) this.variance = config.variance;
      if (typeof config.exitOnPause == "boolean") this.exitOnPause = config.exitOnPause;

      if (config.seconds > 0)
      {
       if (!this.startSec) this.startSec = config.seconds;
       if (!this.startDatetime) this.startDatetime = new Date();
       var currentDatetime = new Date();

       if (currentDatetime.getTime() - this.startDatetime.getTime() > (this.startSec - config.seconds) * this.variance * 1000)
       {
        if (typeof config.onPause == "function") config.onPause();

        if (!this.exitOnPause)
        {
         this.startDatetime = new Date();
         this.startSec = config.seconds--;
         window.setTimeout(function () { that.count(config); }, 1000);
        }
       }
       else
       {
        config.seconds--;
        window.setTimeout(function () { that.count(config); }, 1000);
       }
      }
      else
      {
       if (typeof config.onFinish == "function") config.onFinish();
      }
     }
    }
};

This timer object has a single method, count(), which accepts an object as input. It expects a seconds property in the input object at minimum.

For some reason, window.setTimeout doesn't always work as expected. Sometimes, on my machine, window.setTimeout(x, 1000), which should execute the code after 1 second, took more than 2 seconds. So, in a case like this, you should allow a variance, so people who aren't cheating don't get flagged as cheaters. The variance defaults to 1, but it can be overridden in the input object. Here's an example of how to use this code, which allows 2.5 seconds of "wiggle room" for slow-pokes:

timer.count({
    seconds: 10,
    onPause: function () { alert("You cheated!"); window.location.replace("cheatersAreBad.html"); },
    onFinish: function () { alert("Time's up!"); },
    variance: 2.5
});

With a solution like this, you could use Ajax to tell a server-side script that the user has paused the timer or redirect the user to a page explaining they were caught cheating, for example. If, for some reason, you wanted to allow the user to continue taking the quiz after they've been caught cheating, you could set exitOnPause to false:

timer.count({
    seconds: 10,
    exitOnPause: false,
    onPause: function () { recordCheaterViaAjax(); },
    onFinish: function () { alert("Time's up!"); },
    variance: 2.5
});
Christopher Parker