内联onclick vs addeventlistener的顺序是什么?为什么?

Cap*_*ate 7 html javascript

考虑这个按钮:

<button id="the_button" onclick="RunOnClick();">Click</button>
Run Code Online (Sandbox Code Playgroud)

此按钮具有内联onclick事件处理程序.RunOnClick函数的内容与此问题无关.

如果我还附加了另一个单击事件监听器,如下所示:

var btn = document.getElementById('the_button');

btn.addEventListener("click", function(){
    // Do Something
});
Run Code Online (Sandbox Code Playgroud)

我观察到在内onclick =""之后,使用addEventListener注册的处理程序似乎总是最后运行.再次,'onclick =""'似乎总是首先运行.

我想知道为什么以及我是否总能依赖这个订单.意思是,我可以始终依靠单个内联onclick来先激活,然后在onclick完成后依次添加addEventListener.

这是侥幸吗?或者它实际上是以那种方式设计的,也是ecmascript规范的一部分.

这感觉就像回到了问题的基本质量,但我不知道答案.

Chr*_*isM 8

答案是,是的,这实际上包含在规范中,而不是ECMAScript规范本身,它只是严格处理ECMAScript而不依赖于实现.

首先,DOM事件的排序.我推荐你这个相关的问题.

JavaScript中的事件处理程序是按顺序调用的吗?

正如这说明的那样,以前没有特别说明,但是从DOM级别3规范来看,它确实具体说明它们应该按照它们的注册顺序执行.

但是,如您的示例中定义的内联事件处理程序呢?

为此,我们可以转向HTML 5规范,其中说:

一个事件处理程序的内容属性是特定事件处理程序内容属性.content属性的名称与事件处理程序的名称相同.

[...]

当事件处理程序H的元件或对象的T实现事件目标接口首先被设置为一个非空值,则用户代理必须一个事件监听器附加到与相关联的事件侦听器的列表T类型设置为事件处理程序事件类型相对应的H回调设置为下面定义的事件处理程序处理算法.

打开这个以及随后的注释,这里需要注意的关键事项是:

  • 添加"事件处理程序内容属性"(例如,您的onclick属性)的行为将导致将事件侦听器添加到DOM规范中前面提到的列表中.
  • 幕后实际发生的事情是,此事件侦听器不是严格意义上的onclick属性,而是内部事件处理程序处理算法,用于评估属性和执行适当的回调.
  • 重要的是,当属性更改或设置为null时,事件侦听器及其在列表中的位置不会更改.仅更改事件处理程序处理算法的结果(因为您已更改输入).

这可能有点令人困惑.自己阅读和消化规范中的注释可能会有所帮助,但我也会尝试涵盖下面的关键含义.

首先,是的,您看到的行为被规范有效地定义为规范告诉我们的逻辑结果.解析文档并遇到内联事件处理程序属性时,会立即将此内部算法添加到事件侦听器列表中.根据规范,这将意味着第一个事件监听器将是与您的事件处理程序属性相对应的事件监听器.因为必须在任何调用之前设置addEventListener它,因为不可能调用addEventListener那些不存在的元素.在这些情况下,它将始终首先执行.

当我们在初始解析后开始搞乱内联属性时,会发生有趣的事情.这是HTML5规范本身的一个示例,它出现在我上面引用的位之后:

例8

此示例演示调用事件侦听器的顺序.如果用户单击此示例中的按钮,则页面将显示四个警报,分别带有文本"ONE","TWO","THREE"和"FOUR".

 <button id='test'>Start Demo</button>
    <script>
    var button = document.getElementById('test');
    button.addEventListener('click', function () { alert('ONE') }, false);
    button.setAttribute('onclick', "alert('NOT CALLED')"); // event handler listener is registered here
    button.addEventListener('click', function () { alert('THREE') }, false);
    button.onclick = function () { alert('TWO'); };
    button.addEventListener('click', function () { alert('FOUR') }, false);
    </script>
Run Code Online (Sandbox Code Playgroud)

正如我们所看到的那样,onclick属性的初始值被覆盖,但新的onclick处理程序仍在第一个和第二个侦听器集之间执行addEventListener.这样做的原因是内联事件处理程序将始终位于侦听器列表中,与第一次添加到元素时的位置相同.这是因为从技术上讲,如前所述,实际的事件监听器不是我们在属性内容中指定的回调,而是一个内部算法,它将属性内容作为输入.

我已经创建了一个JSFiddle来测试这种情况,我可以确认这是我在Firefox和Chrome中看到的行为.


总而言之,实际上:

  1. 首次加载时在文档源中遇到的事件处理程序属性将始终首先执行,因为它们之前无法添加事件侦听器.
  2. 对于稍后添加的事件处理程序属性,例如setAttribute,它们将遵循它们被添加的顺序,分别对应于之前和之后的调用addEventListener.
  3. 但是,更改或取消设置事件处理程序属性的值不会更改它在事件侦听器列表中的位置.

希望这能说明问题!