Javascript事件未按预期顺序触发

时间:2009-07-22 14:06:12

标签: javascript events javascript-events

所以我的当前项目还有一个小问题:JS事件没有按照我期望的顺序触发。

假设我有一个带有onBlur事件的文本字段,该事件触发AJAX调用,该调用在服务器上进行一些计算(我使用.NET 3.5的带有JSON的Web服务)。它的回调方法使用计算结果填充一个包含文本字段的页面。

我还有一个带有onClick事件的按钮,它启动了一个不同的计算,但它依赖于在其onClick事件的结果有效之前完成了文本字段的onBlur事件。

我有一个简单的互斥锁,它运行得相当好,直到下列情况:用户更改其中一个文本字段中的值但尚未离开它,因此焦点仍然在文本字段上。当文本字段具有焦点时,它们会单击具有onClick事件的按钮。 浏览器(本例中为IE7)通常会在文本字段的onBlur事件之前触发按钮的onClick事件!

这会造成一些破坏,我不知道如何强制执行被触发事件的顺序。我的小互斥体的工作方式如下:当任何事件被触发时,我设置一个标志,表示“等待回调”。在执行任何操作之前触发检查标志的任何后续事件。在回调中,将标志重置为false。

那么,有什么想法吗?我想我几乎想让所有的网络服务调用同步,但我不知道是否有一种简单的方法可以做到这一点。

我尝试过的其他事情或你应该知道的事情: - 禁用文本字段的onBlur中的按钮 - 它们的onClicks在被禁用之前仍然会触发 - 请记住,这仅在编辑文本字段并立即单击按钮时发生。如果用户在单击按钮之前单击空白区域以触发文本字段的onBlur,则一切正常。

修改

经过一些评论和更多测试后,我希望能够做到:

在文本字段的onBlur中,我想禁用该按钮,以便它的onClick事件不会触发。但是,当我对其进行编码时,onBlur事件会触发,我可以看到该按钮被禁用,但其onClick事件仍在触发。

这是一些示例代码。 有时onClick事件会发生,有时则不会。如果你将setTimeout提升到500ms左右,它似乎总是阻止它。也许这是一个选择......

<html>
<head>
<script type="text/javascript">

var onClickFlag = false;

function whatHappensOnFocus(el) {
    log('whatHappensOnFocus(): TextField focused.');
    onClickFlag = false;
    log('whatHappensOnFocus(): Set onClickFlag = false');
}

function whatHappensOnBlur(el) {
    log('whatHappensOnBlur(): TextField blurred.');
    document.getElementById('theButton').disabled = true;
    log('whatHappensOnBlur(): Disabled the Button');
    setTimeout(function() { someTestFieldFunction(); log('whatHappensOnBlur(): OnBlur callback called: someTestFieldFunction()'); }, 50);
    log ('whatHappensOnBlur(): setTimeout for someTestFieldFunction() for 50ms');
}

function log(msg) {
    document.getElementById('log').value += ('[' + (new Date().getTime()).toString() + '] ' + msg + '\n');
}

function someTestFieldFunction() {
    log('someTestFieldFunction(): Inside someTestFieldFunction()');
    log('someTestFieldFunction(): Test: onClickFlag = ' + onClickFlag);
    //alert('you blurred the text field.');
    //alert('onClickFlag = ' + onClickFlag);
    setTimeout(enableButton, 50);
    log('someTestFieldFunction(): setTimeout for enableButton()');
}

function enableButton() {
    document.getElementById('theButton').disabled = false;
    log('enableButton(): Button re-enabled');
    //onClickFlag = false;
}

function whatHappensOnClick(el) {
    log('whatHappensOnClick(): the Button onClick fired');
    someFunction();
    log('whatHappensOnClick(): someFunction() called');
}

function whatHappensOnMouseDown(el) {
    log('whatHappensOnMouseDown(): inside whatHappensOnMouseDown()');
    onClickFlag = true;
    log("whatHappensOnMouseDown(): set onClickFlag = true");
}

function someFunction() {
    log('someFunction(): inside someFunction()');
    log('someFunction(): You have successfully clicked the button');
    //alert("you clicked me! this shouldn't happen!")
}

</script>
</head>
<body>
<form id="testform">
<input type="text" size="10" onfocus="whatHappensOnFocus(this);" onblur="whatHappensOnBlur(this);" />
<br />
<br />
<input id="theButton" type="button" onmousedown="whatHappensOnMouseDown(this);" onmouseout="onClickFlag = false;" value="calculate" onclick="whatHappensOnClick(this);" />
<br />
<br />
<textarea rows="15" cols="100" id="log" style="overflow: scroll"></textarea>
<input type="reset" value="clear" />
</form>
</body>
</html>

更新

我更新了我发布的代码以反映一些问题,所以你可以看看我是如何测试的。当您关注文本字段然后单击按钮时,按钮的onMouseDown事件将在文本字段的onBlur事件之前触发。因此,我可以设置一个标志,以便我知道他们试图点击按钮。然后在文本字段的onBlur事件触发的AJAX调用的onSuccess回调事件中,我可以看到是否设置了按钮标志(所以我知道他们试图点击它)并触发该事件(禁用其他所有内容以便它可以完成)。表单上只有几个按钮需要这种行为,所以我认为这个解决方案现在已经足够了。

6 个答案:

答案 0 :(得分:4)

您是否使用AddEventListener来设置活动?如果是这样,你为第三个参数传递了什么?

这可能会有所帮助:

http://www.quirksmode.org/js/events_order.html

答案 1 :(得分:1)

使用js导致文本框在完成所需的操作后失去焦点怎么样?

答案 2 :(得分:0)

最简单的方法(可能是最糟糕的,也可能是FAR),是让程序等待一段时间后再继续你想要它做的事情,这样你就可以控制顺序回调。

正如我所说,这可能是最糟糕的做法,但这是我唯一能想到的。这应该是什么样的:

function onClickHandler(callback)
{
    setTimeout(callback, 50);
}

function onBlurHandler(callback)
{
    //...
}

这样,onBlur上的回调函数将始终首先发生。

答案 3 :(得分:0)

我怀疑事件的顺序是你问题的根源。我刚刚在IE7,IE8和Chrome中尝试了以下代码,事件顺序总是模糊然后点击。

<script>
    function dbg(t) {
        document.getElementById("dbg").value += t + "\n";
    }
</script>
<input type="text" onblur="dbg('text.blur')" />
<input type="button" value="PressMe" onclick="dbg('button.click')" />
<textarea id="dbg" cols="50" rows="10">
</textarea>

如果我正确地关注了您,您正在进行一些需要在之前完成其他呼叫的呼叫。这似乎不是一个非常好的方法,你会遇到这种方法的错误或高复杂性的代码。

请尝试解释您的工作(从功能的角度来看)并尝试寻找替代方法,而不是尝试制定复杂的技术方法。

答案 4 :(得分:0)

我假设您的点击事件依赖于模糊事件的结果。在这种情况下,我认为最好的办法是将按钮设置为禁用(或隐藏它),然后在onblur请求完成时启用它。

<input type="submit" disabled="disabled" />

伪Javascript ......

onblur = function() {
  //ajax complete
  mybutton.disabled = false;   
}

答案 5 :(得分:0)

固定超时50毫秒的问题是你依靠时间来解决问题。如果由于某种原因计算机速度慢,或者您在慢速上网本或旧计算机或旧浏览器或移动设备上运行该怎么办?也许您应该使用setInterval而不是setTimer并继续等待,直到您确认实际发生了禁用。

相关问题