要理解事件冒泡,就得先知道事件流。事件流描述的是从页面接收事件的顺序,比如如下的代码:
<body> <div> click me! </div> </body>如果在body和div内都注册了click的事件监听,之后又点击了div区域,是body先响应还是div先响应?有意思的是,当时的浏览器开发团队IE和Netscape提出了差不多完全相反的事件流的概念。IE的事件流是事件冒泡流,而Netscape提出的事件流是事件捕获流。
IE的事件流叫做事件冒泡,即事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点(文档)。如上代码,点击click事件会这样传播:div->body->html->document(虽然我没写html元素,但是页面上默认还是会存在的)
现代的所有浏览器都支持事件冒泡,但还是有些细微差别。IE5.5以及更早版本中的事件冒泡会跳过<html>元素(从body直接跳到document)。IE9、ff、chrome和safari则将事件一直冒泡到window对象。
2、事件捕获Netscape团队则提出另一种事件流-事件捕获。事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件,如果仍以上面的代码举例:document->html->body->div。
虽然事件捕获是Netscape唯一支持的事件流模型,但是IE9、Safari、chrome、opera和ff目前也都支持这种事件流模型。尽管“DOM2级事件”规范要求事件应该从document对象开始传播,但这些浏览器都是从window对象开始捕获事件的。
因为老版本的浏览器不支持事件捕获,所以我们建议使用事件冒泡。
3、DOM事件流“DOM2级事件”规定事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。还是上面的代码作为例子,单击div元素会按照如下顺序触发事件:document->html->body->div->body->html->document。
在DOM事件流中,实际的目标(div)在捕获阶段不会接收到事件。这意味着在捕获阶段,事件到body就停止了,下一个阶段是“处于目标”阶段,于是事件在div上发生,并在事件处理中被看成冒泡阶段的一部分。然后,冒泡阶段发生,事件又传播回文档。但是多数支持DOM事件流的浏览器都实现了一种特定的行为:即使“DOM2级事件”规范明确要求捕获阶段不会涉及目标事件,但IE9、safari、chrome、ff和opera9.5及更高版本都会在捕获阶段触发事件对象上的事件,结果就是有两个机会在目标对象上面操作。(IE9、opera、ff、chrome和Safari都支持DOM事件流,IE8及更早版本不支持DOM事件流)。
4、事件处理程序响应某个事件的函数就叫做事件处理程序。
DOM0级的事件处理程序很简单,onclick就是常用的DOM0级事件处理函数,只会在冒泡阶段被处理。
而“DOM2级事件”定义了两个方法,用于处理指定和删除事件处理程序的操作:addEventListener()和removeEventListener(),所有DOM节点都包含这两个方法,并且它们都接受3个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最后这个布尔值参数如果是true,表示在捕获阶段调用事件处理程序;如果是false,表示在冒泡阶段调用。DOM2级方法添加事件处理程序的好处是可以添加多个事件处理程序,会按照添加顺序被处理(无论是捕获还是冒泡)。这也是为什么DOM0级事件兼容各种浏览器,我们却还是要使用DOM2的原因之一。
var div = document.getElementById('myDiv'); div.addEventListener('click', function() { console.log(this.id); }, true); div.addEventListener('click', function() { console.log('hello world'); }, true);而IE与DOM不同,它有自己的方法:attachEvent()和detachEvent(),这两个方法接受相同的两个参数:事件处理程序名称和事件处理程序函数。由于IE8以及更早版本只支持事件冒泡,所以通过attachEvent()添加的事件处理程序都会被添加到冒泡阶段(所以不需要第三个参数)。
var div = document.getElementById('myDiv'); div.attachEvent('onclick', function() { console.log('hello world'); });注意第一个参数是onclick,而非DOM标准的click。在IE中使用attachEvent()与使用DOM0级方法的主要区别在于事件处理程序的作用域,在使用DOM0级方法的情况下,事件处理程序会在其所属元素的作用域内运行,而在使用attachEvent()方法的情况下,事件处理程序在全局作用域中运行,因此this等于window(这点要特别注意!!!)。attachEvent()也能添加多个事件处理程序,但是事件的执行顺序和添加顺序相反。
5、跨浏览器的事件处理程序