首页 > 代码库 > jacascript 事件流
jacascript 事件流
前言:这是笔者学习之后自己的理解与整理。如果有错误或者疑问的地方,请大家指正,我会持续更新!
事件流
当浏览器发展到第四代时(IE4及Netscape4),浏览器开发团队遇到了一个很有意思的问题:页面的哪一部分会拥有某个特定的事件?想象画在一张纸上的一组同心圆。如果把手指放在圆心上,那么手指指向的不是一个圆,而是纸上的所有圆。
两家公司的浏览器开发团队在看待浏览器事件方面还是一致的。如果单击了某个按钮,他们都认为单击事件不仅仅发生在按钮上,甚至也单击了整个页面。
但有意思的是,IE 和 Netscape 开发团队居然提出了差不多是完全相反的事件流的概念。IE 的事件流是事件冒泡流,而 Netscape 的事件流是事件捕获流。
事件流又称为事件传播,描述的是从页面中接收事件的顺序。DOM2 级事件规定的事件流包括三个阶段:事件捕获阶段(capture phase)、处于目标阶段(target phase)和事件冒泡阶段(bubbling phase)。
首先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标接收到事件,最后一个阶段是冒泡阶段,可以在这个阶段对事件做出响应。
默认是在冒泡阶段对事件做出响应。
事件冒泡
IE 的事件流叫做事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到 window 对象;
所有现代浏览器都支持事件冒泡,但在具体实现在还是有一些差别。IE9、Firefox、Chrome、Safari将事件一直冒泡到 window 对象;
事件捕获
事件捕获的思想是 window 对象应该更早接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于在事件到达预定目标之前就捕获它;
addEventListener() 方法中的第三个参数是指在冒泡阶段还是捕获阶段处理事件处理程序,设置为 true 时,即为捕获阶段,默认为 false 冒泡阶段;
HTML事件处理程序
某个元素支持的每种事件,都可以使用一个与相应事件处理程序同名的 HTML 特性来指定。这个特性的值应该是能够执行的 javascript 代码;
在事件处理程序函数内部,this值等于事件的目标元素;
缺点1:因为用户可能会有HTML元素一出现在页面上时就触发相应的事件,但当时的事件处理程序有可能尚不具备执行条件,就会报错;
缺点2:客户端编程的通用风格是保持 HTML 内容和 javaScript 行为分离,所以应该避免使用 HTML 事件处理程序属性,因为这些属性直接混合了 javascript 和 HTML,且不易扩展;
DOM0级事件处理程序
通过 javascript 指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。这种为事件处理程序赋值的方法是在第四代Web浏览器中出现的,而且至今仍然为所有现代浏览器所支持。原因一是简单,二是具有跨浏览器的优势。
每个元素都有自己的事件处理程序属性,这些属性通常全部小写,将这种属性的值设置为一个函数,就可以指定事件处理程序。
以DOM0级方式添加的事件处理程序会在事件流的冒泡阶段被处理;
可以通过将事件处理程序属性设置为 null 来删除事件处理程序;
缺点:DOM0级事件处理程序的缺点是围绕着每个事件目标对于每种事件类型只能添加一个事件处理程序。
<div id="box" style="height:30px;width:200px;background-color:pink;"></div> <script> var oBox = document.getElementById(‘box‘); oBox.onclick = function(){ this.innerHTML += ‘DOM0级事件处理程序‘; } </script>
DOM2级事件处理程序
DOM2级事件处理程序定义了两个方法用于处理指定和删除事件处理程序的操作:addEventListener() 和 removeEventListener()。IE8及以下浏览器不支持DOM2级事件处理程序。
使用DOM2级事件处理程序的好处是可以添加多个事件处理程序,并按照他们添加的顺序触发;
<div id="box" style="height:200px;width:200px;background-color:pink;"></div> <script> var oBox = document.getElementById(‘box‘); oBox.addEventListener(‘click‘,function(){ this.innerHTML += ‘使用DOM2级事件处理程序的好处是可以添加多个事件处理程序<br/>‘; }); oBox.addEventListener(‘click‘,function(){ this.innerHTML += ‘并按照他们添加的顺序触发,第三个参数默认为false,所以是事件冒泡的顺序‘; }); </script>
所有DOM节点中都包含这两个方法,并且它们都接受3个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。
最后的布尔值参数如果是 true,表示在捕获阶段调用事件处理程序;如果是 false,表示在冒泡阶段调用事件处理程序。若最后的布尔值不填写,则默认为 false。
<div id="box" style="height:200px;width:200px;background-color:pink;"> <div id="child" style="height: 100px;width: 100px;background-color: green;"></div> </div> <script> var oBox = document.getElementById(‘box‘); var oChild = document.getElementById(‘child‘); oBox.addEventListener(‘click‘,function(){ console.log(‘事件捕获顺序 先oBox‘); },true); oChild.addEventListener(‘click‘,function(){ console.log(‘事件捕获顺序 后oChild‘); },true); </script>
若最后的布尔值不填写,则和 false 效果一样。
<script> var oBox = document.getElementById(‘box‘); var oChild = document.getElementById(‘child‘); oBox.addEventListener(‘click‘,function(){ console.log(‘事件冒泡顺序 后oBox‘); }); oChild.addEventListener(‘click‘,function(){ console.log(‘事件冒泡顺序 先oChild‘); }); </script>
通过 addEventListener() 添加的事件处理程序只能使用 removeEventListener() 来移除,移除时传入的参数与添加处理程序时使用的参数相同。addEventListener() 添加的匿名函数将无法移除;
<div id="box" style="height:200px;width:200px;background-color:pink;"></div> <script> var oBox = document.getElementById(‘box‘); oBox.addEventListener("click",function(){ this.innerHTML += ‘removeEventListener()无法移除匿名函数‘; },false); oBox.removeEventListener(‘click‘,function(){ this.innerHTML += ‘removeEventListener()无法移除匿名函数‘; },false); </script>
IE事件处理程序
IE实现了与DOM中类似的两个方法:attachEvent() 和 detachEvent()。这两个方法接受相同的两个参数:事件处理程序名称与事件处理程序函数。由于IE8及以下浏览器只支持事件冒泡,所以通过 attachEvent() 添加的事件处理程序都会被添加到事件冒泡阶段;
attachEvent() 方法的第一个参数是"onclick",而非DOM的 addEventListener()方法中的"click";
与其他三个事件处理程序不同,IE事件处理程序的 this 指向 window,而非被绑定事件的元素;
使用 attachEvent() 方法添加的事件处理程序的触发顺序是有区别的。IE9、10浏览器是按正序执行的,而IE8及以下浏览器则是按倒序执行的;
<div id="box" style="height:30px;width:200px;background-color:pink;"></div> <script> var oBox = document.getElementById(‘box‘); oBox.attachEvent(‘onclick‘,function(){ this.innerHTML += ‘attachEvent()方法的第一个参数是"onclick"‘; //与其他三个事件处理程序不同,IE事件处理程序的this指向window,而非被绑定事件的元素 console.log(this);//window }) </script>
使用 attachEvent() 添加的事件可以通过 detachEvent() 来移除,条件是必须提供相同的参数。与DOM2级事件处理程序一样,这也意味着添加的匿名函数将不能被移除。不过,只要能够将对相同函数的引用传给 detachEvent(),就可以移除相应的事件处理程序。
<div id="box" style="height:30px;width:200px;background-color:pink;"></div> <script> var oBox = document.getElementById(‘box‘); oBox.attachEvent("onclick",function(){ box.innerHTML += ‘与DOM2级事件处理程序一样,attachEvent添加的匿名函数将不能被移除‘; },false); oBox.detachEvent(‘onclick‘,function(){ box.innerHTML += ‘与DOM2级事件处理程序一样,attachEvent添加的匿名函数将不能被移除‘; },false); </script>
事件执行顺序
如果同时出现 HTML 事件处理程序和 DOM0 级事件处理程序,DOM0 级会覆盖 HTML 事件处理程序;
chrome/opera/safari等 webkit 内核的浏览器会按照事件处理程序出现的顺序来排列,所以结果为:DOM2级 DOM0级
firefox 浏览器和 IE 浏览器会将 DOM0 级事件优先调用,所以 firefox 和 IE11 浏览器结果为:DOM0级 DOM2级
IE9、10浏览器结果为:DOM0级 DOM2级 IE
IE8及以下浏览器结果为:DOM0级 IE
jacascript 事件流