JS 事件流和事件处理程序

事件流

当你点击一个按钮时,实际上不光点击了这个按钮,还点击了他的容器以及整个页面。事件流描述了页面接收事件的顺序。IE支持事件冒泡流,NetScape支持事件捕获流。

事件冒泡

事件被定义为从最具体的元素(文档树中最深的节点)开始触发,然后向上传播至没有那么具体的元素(文档)。

IE事件流被称为事件冒泡。所有现代浏览器都支持事件冒泡,现代浏览器中的事件会一直冒泡到window对象。IE5.5及早期版本会跳过html元素。

事件捕获

事件捕获的意思是最不具体的节点应该最先收到事件,而最具体的节点应该最后收到事件。事件捕获实际上是为了在事件到达最终目标前拦截事件。

NetScape communicator团队提出了另一种名为事件捕获的事件流。事件捕获得到了所有现代浏览器的支持。实际上,所有浏览器都是从window对象开始捕获事件,而DOM2 Events规范规定的是从document开始。

由于旧版本浏览器不支持,因此实际当中几乎不会使用事件捕获。通常建议使用事件冒泡,特殊情况下可以使用事件捕获。

DOM 事件流

DOM2 Events规范规定事件流分为3个阶段:事件捕获、到达目标和事件冒泡。事件捕获最先发生,为提前拦截提供了可能。然后,实际的目标元素接收到事件,最后一个阶段是冒泡,最迟要在这个阶段相应事件。

在DOM事件流中,实际的目标(<div>元素)在捕获阶段不会接收到事件,这是因为捕获阶段从document到<html>到<body>就结束了。下一阶段,即会在div元素上触发事件的到达目标阶段,通常在事件处理时被认为是冒泡阶段的一部分。然后,冒泡阶段开始,事件反向传播至文档。

大多数支持DOM事件流的浏览器实现了一个小小的扩展。虽然DOM2 Events规范明确捕获阶段不命中事件目标,但现代浏览器都会在捕获阶段在事件目标上触发事件。最终结果是在事件目标上有两个机会来处理事件。

注意 所有现代浏览器都支持DOM事件流,只有IE8及更早版本不支持。

事件处理程序

事件意味着用户或浏览器执行的某种动作,如单击、加载。为响应事件而调用的函数被称为事件处理程序(或事件监听器)。

作为事件处理程序执行的代码可以访问全局作用域中的一切。

DOM0事件处理程序

像这样使用DOM0方式为事件处理程序赋值时,所附函数被视为元素的方法。

let btn = document.getElementById("myBtn");
btn.onclick = function() {
    console.log("Clicked");
};

通过将事件处理程序属性的值设置为null,可以移除通过DOM0方式添加的事件处理程序。

btn.onclick = null;

DOM2事件处理程序

DOM2 Events为事件处理程序的赋值和移除定义了两个方法:addEventListener()removeEventListener()。这两个方法暴露在所有DOM节点上,它们接收3个参数:事件名、事件处理程序和一个布尔值,true表示在捕获阶段调用事件处理程序,false表示在冒泡阶段调用事件处理程序。

let btn = document.getElementById("myBtn");
btn.addEventListener("click", () => {
    console.log(this.id);
}, false);

与DOM0方式类似,这个事件处理程序同样在被附加到的元素的作用域中运行。使用DOM2方式的主要优势是可以为同一个事件添加多个事件处理程序。多个事件处理程序以添加顺序来触发。

通过addEventListener()添加的事件处理程序只能使用removeEventListener()并传入与添加时同样的参数来移除。这意味着使用addEventListener()添加的匿名函数无法移除

大多数情况下,事件处理程序会被添加到事件流的冒泡阶段,主要原因是跨浏览器兼容性好。把事件处理程序注册到捕获阶段通常用于在事件到达其制定目标前拦截事件,如不需要,则不要使用事件捕获。

IE事件处理程序

IE实现了与DOM类似的方法,即attachEvent()detachEvent()。这两个方法接收相同的参数:事件处理程序的名字和事件处理函数。因为IE8及更早版本只支持事件冒泡,所以使用attachEvent添加的事件处理程序会添加到冒泡阶段。

var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function() {
    console.log("Clicked");
});

IE中使用attachEvent与使用DOM0方式的主要区别是事件处理程序的作用域。使用DOM0方式时,事件处理程序中的this值等于目标元素。而使用attachEvent时,事件处理程序是在全局作用域中运行的,因此this等于window

使用attachEvent也可以给一个元素添加多个事件处理程序。与DOM方式不同的是,这里的事件处理程序会以添加他们的顺序反向触发