首页 > 解决方案 > Javascript事件队列:XHR响应在父onclick之前排队

问题描述

对于以下测试用例(我将 XHR 调用替换为 setTimeout 以表示响应非常快的 XHR 调用):

<html>
  <body>
     <div onclick="console.log('onclick parent')">
       Event Queue Test:
       <input type=text onblur="console.log('onblur'); setTimeout(function() {console.log('onblur timeout')})"/>
     </div>
   </body>
</html>

如果我单击文本字段,然后单击文本字段前面的文本,我会在 Chrome (v70) 中获得以下输出:

onblur
onblur timeout
onclick parent

我本来希望父 onclick 处理程序在 setTimeout 事件之前排队。至少在 Chrome 中,它没有。这种行为是由标准指定的还是依赖于实现的?我也对始终让父 onclick 处理程序在超时(或 XHR)处理程序之前运行的变通方法感兴趣。

在我的实际用例中,这个问题是有问题的,因为不能保证事件以相同的顺序排队,这取决于 XHR 请求运行多长时间。

标签: javascripthtmldom-events

解决方案


这不一定会帮助您解决实际的 XHR 问题,但我想我了解发生了什么。为了方便起见,我使用 jQuery 扩展了您的示例代码:

<div class=parent>Label: <input class=field></div>

和 JavaScript:

var $d = $(document);

var t = {
  mousedown: -1,
  mouseup: -1,
  focus: -1,
  click: -1,
  blur: -1,
  timer: -1
};

["mousedown", "mouseup", "focus", "click"].forEach(function(event) {
    $d.on(event, ".parent", function() {
    t[event] = Date.now();
  })
});
$d.on("blur", ".field", function() {
    t.blur = Date.now();
  setTimeout(function() {
    t.timer = Date.now();
  }, 150);
});
$d.on("click", ".parent", function() {
    setTimeout(function() {
    var race = Object.keys(t).sort(function(k1, k2) { return t[k1] - t[k2];});
    console.log("Result: ", race);
    race.forEach(function(key) { t[key] = -1; });
  }, 1000);
});

该代码所做的是按事件实际发生的时间对事件进行排序。在输入框点击聚焦后点击文本的情况下,超时时间短(小于100ms左右)的顺序为

  1. 重点
  2. 鼠标按下
  3. 模糊
  4. 计时器
  5. mouseup
  6. 点击

“模糊”和“计时器”事件用于输入字段及其setTimeout()回调。如果我让超时间隔更长,比如 150 毫秒左右,那么我会得到

  1. 重点
  2. 鼠标按下
  3. 模糊
  4. mouseup
  5. 点击
  6. 计时器

我认为这是有道理的:“mousedown”和“mouseup”事件是由您与鼠标按钮的实际物理交互驱动的。在收到“mouseup”之前,“click”事件没有意义。使用“模糊”处理程序中的短计时器,计时器可以在鼠标通过您的操作系统等物理传达“向上”事件之前触发。


推荐阅读