防止js alert()暂停计时器

5
我为一场测验制作了一些计时器。问题是,我刚意识到当我放置

标签时,我的代码就无法正常工作了。
javascript: alert("blah");

在地址栏中,弹出的警报框会暂停我的计时器。在测验中这是非常不希望的。
我认为没有办法阻止这种行为……但我还是要问一下。
如果确实如此,你能建议我应该怎么做吗?
8个回答

10

永远不要依赖JavaScript(或任何其他客户端时间)来计算在postbacks或不同页面之间进行的操作的经过时间。

如果您始终比较服务器日期,则很难被人欺骗:

  1. 第一次页面请求,存储服务器时间
  2. 每N秒使用JavaScript调用ping,比较2个服务器时间,并返回经过的时间(仅供展示)
  3. 当用户提交表单时,比较2个服务器时间,计算经过的时间,并丢弃需要太长时间的时间(即:可能是作弊者)

6

不,无法阻止JavaScript中单个线程的警报。可以使用其他方式进行用户通知,例如浮动层。


实际上它确实可以,这似乎是一个两部分的问题 - 1)我能停止这种行为吗?2)如果不能,我还能做什么来显示通知? - matt b

6

显然,预览渲染与发布渲染不同。此段落旨在确保接下来的两行显示为代码。

// 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).
};

注意:虽然我知道警报仍然可以通过javascript: nativeAlert();访问,但至少它需要查看源代码和一些JS理解,而不是盲目尝试alert(); - syaz
您还可以将nativeAlert包装在闭包中,使其成为私有的。 - eyelidlessness
这很好,但正确的答案是Filini的。始终假设不在您直接控制下的浏览器是腐败的、被操纵的或其他问题。而“直接控制”是指在BEAT-WITH-STICK距离内。 - Anders Eurenius
同意,实际上。但我确实想直接回答这个具体问题(如何覆盖警报)。 - eyelidlessness

4

这是一个模态框,会暂停执行。考虑使用不会暂停执行的替代方法,比如Lightbox技术。


3
我认为提问者是在试图防止作弊。由于用户可以在地址栏中输入javascript:alert(“paused”)或制作一个书签工具来实现,所以很容易暂停测验并作弊。
我能想到的唯一方法是使用Date()获取当前时间,并在计时器触发时再次检查。然后,如果时间差不合理地接近预定的计时器持续时间,则显示警告并取消该问题的答案,或让他们不及格。无法阻止用户暂停您的测验,但应该能够抓住他们。
当然,在任何防作弊方面,您都会激励人们成为更好的作弊者。一个人可以更改其PC上的系统时间,并欺骗从操作系统获取时间的javascript Date()构造函数。
您可以使用间隔对一秒钟的间隔长度进行重复的时钟比较。间隔处理程序还可以更新用户显示屏上的剩余时间字段。然后用户可以感受到随着测验时间的流逝而增加的压力。快乐时光!

很高兴你问了这个问题。我以前从没想过用alert作为暂停按钮。也很高兴你问了这个问题,因为被选中的答案是我之前从未见过的。现在我可以通过制作一个alert书签来欺骗定时的JavaScript测验!(直到他们读到这个页面) - Mnebuerquo

2

对SyaZ的问题的反馈循环澄清了所涉及的问题。

以下是总结迄今为止好的答案的尝试:

  • 客户端脚本天生容易被操纵以作弊在线测验。请参见@Filini的服务器端方法

  • window.alert = function(msg) {}将覆盖alert(),可能会击败低效果的放在地址栏中:javascript:alert('暂停页面,以便我可以谷歌答案')或者我现在要使用我的“打电话给朋友”。 参见Courtesy of @eyelidlessness

  • 如果必须使用客户端方法,而不是使用setTimeOut(),您可以使用一个自定义日期比较的暂停函数,如此(concept by @Mnebuerquo,code example by me (@micahwittman)):

例如:

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);

2

最终,您不能信任用户输入。如果没有在服务器上跟踪经过的时间,就无法保证数据没有被篡改。

然而,如果您确信您的测验参与者不精通JavaScript,并且仅仅依靠他们在某个地方发现的“诀窍”,您可以使用以下代码测试作弊(暂停),这不需要修改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();
            }
        }
    }
};

这个计时器对象只有一个方法,count(),接受一个对象作为输入。它期望输入对象中至少有一个seconds属性。

出于某种原因,window.setTimeout并不总是按预期工作。有时,在我的机器上,应该在1秒后执行代码的window.setTimeout(x, 1000)需要2秒以上的时间。因此,在这种情况下,您应该允许一定的变化范围,以便那些没有作弊的人不会被标记为作弊者。变化范围默认为1,但可以在输入对象中进行覆盖。以下是如何使用此代码的示例,它允许慢速用户有2.5秒的“摇晃空间”:

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

使用这样的解决方案,您可以使用Ajax告诉服务器端脚本用户已暂停计时器或重定向用户到一个页面,说明他们被抓到作弊了。如果出于某种原因,您想允许用户在被抓到作弊后继续参加测验,则可以将exitOnPause设置为false:

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

0
服务器会话可以设置为在1小时后过期。JavaScript 可以仅用作用户了解剩余时间的显示工具。如果他决定通过暂停计时器来作弊,那么当他发布测试时,他可能会惊讶地发现他的会话已经超时了。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接