多个事件监听器的顺序

23

在使用Prototype处理点击事件时,我遇到了一个奇怪的问题。如果你点击下面代码中的按钮,它将触发三个警报:'Click 1'、'Click 2'和'Click 3'。现代浏览器将按照注册顺序调用侦听器,而IE8(以及可能早期版本的IE)将按相反的顺序调用侦听器。我认为这很奇怪,因为我认为Prototype会维护和执行侦听器队列,这应该是与浏览器无关的。这种情况不是这样吗?如果不是,那么事件监听器是否应按某种顺序运行,还是它们是异步的,因此其顺序无关紧要?

    <button id="button">Click me</button>
    <script type="text/javascript">
        $('button').observe('click', function(event) {
            alert('Click 1');
        });
        $('button').observe('click', function(event) {
            alert('Click 2');
        });
        $('button').observe('click', function(event) {
            alert('Click 3');
        });
    </script>
4个回答

35

原型依赖于浏览器的底层触发机制来确定顺序(并非所有库都是如此,见下文)。最初,DOM事件规范并不保证事件处理程序被触发的顺序(但请继续阅读)。来自DOM2事件规范

虽然在任何接收到该EventTarget的事件时,所有EventTarget上的EventListener都保证会被触发,但并没有规定它们将按照哪些EventListeners在EventTarget上的顺序接收到事件。

大多数浏览器实现(Chrome,Firefox,Opera等,包括IE9)按照附加的顺序触发处理程序。 IE8及更早版本则相反。

较新(现在已经非常成熟的)DOM3事件规范添加了注册顺序触发的要求(大多数浏览器都这样做):

接下来,实现必须确定当前目标的候选事件侦听器。 这必须是按其注册顺序在当前目标上注册的所有事件侦听器的列表。

这可能是为什么IE9现在也会这样做的一部分原因(IE9显着改进了Microsoft对事件标准的支持,添加了addEventListener等功能)。

一些JavaScript库(例如jQuery)无论浏览器如何都保证顺序,通过每个元素每个事件只附加一个处理程序并维护自己的用户代码处理程序列表来实现。


1

0
除了已经讨论的内容,我想指出不同浏览器处理元素onclick属性顺序的差异,当其他事件监听器被注册时。据我所能测试的:
  • Firefox将onclick视为另一个事件监听器,并按照设置的顺序执行回调。
  • Chrome在所有已注册的监听器之后执行onclick回调。
  • IE8在所有已注册的监听器之前执行onclick回调(这些监听器按其他答案中所述的相反顺序执行)。
因此,在以下示例中,当单击按钮时:
<!DOCTYPE html>
<html>
<script>
function ini() {
    let btn = document.getElementById("btn");
    if (btn.addEventListener) {
        btn.addEventListener("click", func1, true);
    } else {
        btn.attachEvent("onclick", func1);
    }
    btn.onclick=func2;
    if (btn.addEventListener) {
        btn.addEventListener("click", func3, true);
    } else {
        btn.attachEvent("onclick", func3);
    }
}

function func1() {
    console.log("Called 1");
}
function func2() {
    console.log("Called 2");
}
function func3() {
    console.log("Called 3");
}
</script>
<body onload="ini();">
<input type="button" id="btn" value="button">
</body>
</html>

Firefox 将显示:

Called 1
Called 2
Called 3

Chrome 将显示:

Called 1
Called 3
Called 2

而IE8将显示:

Called 2
Called 3
Called 1

0
根据这个错误报告,看起来似乎是一个已存在的bug,并且事实上是与浏览器相关的。
总结报告如下:
虽然它与浏览器有关,但一些用户(比如发帖者)期望事件按顺序触发。这个bug条目曾经被改为文档更改(通知用户顺序事实上是未指定的),但该bug仍然存在,所以我认为在文档中它还没有被修复。

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