javascript - 内联 onclick 与 addeventlistener 的顺序是什么?为什么?

标签 javascript html

考虑这个按钮:

<button id="the_button" onclick="RunOnClick();">Click</button>

此按钮有一个内联的 onclick 事件处理程序。 RunOnClick 函数的内容对于这个问题无关紧要。

如果我还像这样附加另一个点击事件监听器:

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

btn.addEventListener("click", function(){
    // Do Something
});

使用 addEventListener 注册的处理程序似乎总是在内联 onclick="" 之后运行。

我能否始终依靠单个内联 onclick 处理程序先触发,然后再触发 addEventListener 处理程序?

这是侥幸吗?或者它实际上是这样设计的并且是 ECMAScript 规范之一的一部分?

这感觉像是一个回到基础的问题,但我不知道答案。

最佳答案

答案是,是的,这实际上包含在规范中,而不是 ECMAScript 规范本身,它只严格处理与实现无关的 ECMAScript。

首先,DOM事件的排序。我建议你引用这个相关的 SO 问题。

Are event handlers in JavaScript called in order?

正如这里所述,之前它是未指定的,但从 DOM level 3 spec 开始它确实明确指出它们应该按照它们注册的顺序执行。

但是像您的示例中那样定义的内联事件处理程序呢?

为此我们可以求助于 HTML 5 spec其中说:

An event handler content attribute is a content attribute for a specific event handler. The name of the content attribute is the same as the name of the event handler.

[...]

When an event handler H of an element or object T implementing the EventTarget interface is first set to a non-null value, the user agent must append an event listener to the list of event listeners associated with T with type set to the event handler event type corresponding to H and callback set to the event handler processing algorithm defined below.

解开这个和随后的注释,这里要带走的关键是:

  • 添加“事件处理程序内容属性”(例如您的 onclick 属性)的行为将导致事件监听器添加到 DOM 规范前面提到的列表中。
  • 在幕后实际发生的是,这个事件监听器严格来说并不是您在 onclick 属性中指定的代码,而是一个内部事件处理程序处理算法,用于评估该属性并执行适当的回调。
  • 重要的是,当属性更改或设置为 null 时,事件监听器及其在列表中的位置不会更改。仅更改事件处理程序处理算法的结果(因为您更改了输入)。

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

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

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

EXAMPLE 8

This example demonstrates the order in which event listeners are invoked. If the button in this example is clicked by the user, the page will show four alerts, with the text "ONE", "TWO", "THREE", and "FOUR" respectively.

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

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

我创建了一个 JSFiddle测试情况是否如此,我可以确认这是我在 Firefox 和 Chrome 中看到的行为。


所以总结一下,在实践方面:

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

希望一切都解决了!

关于javascript - 内联 onclick 与 addeventlistener 的顺序是什么?为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49805942/

相关文章:

javascript - Mottie 的虚拟键盘 : direct input

javascript - 比较两个对象数组;如果第二个对象包含与第一个对象相同的键/值,则返回第二个对象,如果没有,则添加第一个对象的键/值

javascript - 将日历功能集成到静态 .html 站点中

javascript - 制作一个链接,点击后消失并显示一个 div

javascript - 使用 async/ajax 时保持不变的粘性页脚?

javascript - 多个 <select> 元素验证使表单提交变得复杂

javascript - 使用 Stripe 接受在线支付

javascript - 设置函数默认this值

php - HTML php 本地主机数据库检索

html - 动态生成的 HTML 的格式 - 没人关心吗?