内容简介:当浏览器发展到第四代时(IE4及Netscape Communicator4),浏览器开发团队遇到了一个很有意思的问题:页面的哪一部分会拥有某个特定的事件?要明白这个问题问的是什么,可以想象画在一张纸上的一组同心圆。如果你把手指放在圆心上,那么你的手指指向的不是一个圆,而是纸上的所有圆。两家公司的浏览器开发团队在看待浏览器事件方面还是一致的。如果你单击了某个按钮,他们都认为单击事件不仅仅发生在按钮上。换句话说,在单击按钮的同时,你也单击了按钮的容器元素,甚至也单击了整个页面。事件流描述的是从页面中接收事件的
当浏览器发展到第四代时(IE4及Netscape Communicator4),浏览器开发团队遇到了一个很有意思的问题:页面的哪一部分会拥有某个特定的事件?要明白这个问题问的是什么,可以想象画在一张纸上的一组同心圆。如果你把手指放在圆心上,那么你的手指指向的不是一个圆,而是纸上的所有圆。两家公司的浏览器开发团队在看待浏览器事件方面还是一致的。如果你单击了某个按钮,他们都认为单击事件不仅仅发生在按钮上。换句话说,在单击按钮的同时,你也单击了按钮的容器元素,甚至也单击了整个页面。
事件流描述的是从页面中接收事件的顺序。但有意思的是,IE和Netscape开发团队居然提出了差不多是完全相反的事件流的概念。IE的事件流是事件冒泡流,而Netscape Communicator的事件流是事件捕获流。
思考:点击页面元素,什么样的元素能感应到这样一个事件?
点击页面元素的同时,也点击了元素的容器元素甚至整个页面。
事件冒泡
IE的事件流叫做 事件冒泡 (event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。
事件捕获
Netscape Communicator 团队提出的另一种事件流叫做 事件捕获 (event capturing)。其思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于事件到达预订目标之前捕获它。
DOM 事件流
“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。首先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标接收到事件。最后一个阶段是冒泡阶段,( 一般情况下,程序 )可以在冒泡阶段对事件作出响应。
事实上,DOM事件流在捕获阶段和冒泡阶段均可触发事件处理程序。捕获阶段先收到事件流
对上面元素添加事件绑定程序。
var outBox = document.getElementById("outBox"); var middleBox = document.getElementById("middleBox"); var innerBox = document.getElementById("innerBox"); outBox.addEventListener('click', function() { console.log('outBox捕获'); }, true); middleBox.addEventListener('click', function() { console.log('middleBox捕获'); }, true); innerBox.addEventListener('click', function() { console.log('innerBox捕获'); }, true); outBox.addEventListener('click', function() { console.log('outBox冒泡'); }, false); middleBox.addEventListener('click', function() { console.log('middleBox冒泡'); }, false); innerBox.addEventListener('click', function() { console.log('innerBox冒泡'); }, false); // 输出顺序 // 1 outBox捕获 // 2 middleBox捕获 // 3 innerBox捕获 // 4 innerBox冒泡 // 5 middleBox冒泡 // 6 outBox冒泡 复制代码
IE9、Opera、Firefox、Chrome和Safari( 也就是说当下主流浏览器 )都支持DOM事件流;IE8及更早版本不支持DOM事件流。
思考一下:事件冒泡和事件捕获在日常开发中有哪些应用场景?
我们下面先看一下事件处理程序。
事件处理程序
1. HTML 事件处理程序
为元素添加(事件)属性,值为事件处理程序。
注:事件属性全部为小写。
<div class="outBox" onclick="alert('outBox')"> <div class="innerBox" onclick="showInnerBox()"></div> </div> <script> function showInnerBox() { alert("innerBox"); } </script> 复制代码
特点:传统方式,简单,跨浏览器
2. DOM0 级事件处理程序
使用JavaScript指定事件处理程序,首先必须取得一个要操作的对象的引用。
<div class="outBox" id="outBox"> <div class="innerBox" id="innerBox"></div> </div> <script> var outBoxElement = document.getElementById('outBox'); outBoxElement.onclick = function() { alert(this.id) } // 删除事件处理程序 outBoxElement.onclick = null </script> 复制代码
特点:可通过 this
访问元素的任何属性和方法(HTML事件处理程序也可以),可以删除事件处理程序
3. DOM2 级事件处理程序
“DOM2级事件”定义了两个方法,用于处理指定和删除事件处理程序的操作: addEventListener()
和 removeEventLintener()
。所有DOM结点中都包含这两个方法,并且他们都接受3个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最后这个布尔值如果是 true
,表示在捕获阶段调用事件处理程序;如果是 false
,表示在冒泡阶段调用事件处理程序。
// 添加事件绑定 outBoxElement.addEventListener('click', showOutBox, false); // 移除事件绑定 outBoxElement.removeEventListener('click', showOutBox); // 下面移除事件绑定方式无效,无法移除绑定的匿名函数 outBoxElement.removeEventListener('click', function() { alert(this.id); }); 复制代码
4. IE 事件处理程序
IE实现了与DOM中类似的两个方法:attachEvent() 和 detachEvent()。这两个方法接受相同的两个参数:事件名称和事件处理函数。由于IE8及更早版本只支持事件冒泡,所以通过attachEvent()添加的事件处理程序都会被添加到冒泡阶段。
var btn = document.getElementById('MyBtn'); btn.attachEvent('onclick', function() { console.log('clicked'); }); var handler = function() { console.log('clicked'); } // 第二个参数必须是具名函数,不能是匿名函数 btn.detachEvent('onclick', handler); 复制代码
注:attachEvent() 的第一个参数是'onclick',而非DOM的addEventListener()方法中的'click'。
在IE中使用attachEvent()与使用DOM0级方法的主要区别在于时间处理程序的作用域。 在使用DOM0级方法的情况下,事件处理程序会在其所属元素的作用域内运行;在使用attachEvent()方法的情况下事件处理程序会在全局作用域中运行,因此this等于window 。
btn.attachEvent('onclick', function() { console.log(this === window); // true }) 复制代码
此外,attachEvent() 与addEventListener() 类似均可以为一个元素添加多个事件处理程序。
// 为同一个元素添加多个事件处理程序 btn.addEventListener('click', function() { console.log('clicked'); }, false); btn.addEventListener('click', function() { console.log('hello world'); }, false); btn.attachEvent('onclick', function() { console.log('clicked'); }); btn.attachEvent('onclick', function() { console.log('hello world'); }); 复制代码
5. 跨浏览器事件处理程序
跨浏览器事件处理程序即一套可以同时运行在IE以及非IE浏览器的程序,重点是浏览器能力检测也就是浏览器兼容性检查。
var EventUtil = { // 添加事件绑定(元素,事件类型,事件处理函数) addHandler: function(element, type, handler) { if(element.addEventListenter) { // DOM2级绑定事件 element.addEventListenter(type, handler, false); }else if(element.attachEvent) { // IE绑定事件 element.attachEvent('on' + type, handler) }else { // DOM0级绑定事件,兼容IE8及更早版本 element['on' + type] = handler; } }, // 删除事件事件绑定(元素,事件类型,事件处理函数) removeHandler: function() { if(element.addEventListenter) { // DOM2级删除绑定事件 element.removeEventListenter(type, handler, false); }else if(element.attachEvent) { // IE删除绑定事件 element.detachEvent('on' + type, handler) }else { // DOM0级删除绑定事件,兼容IE8及更早版本 element['on' + type] = null; } } } var btn = document.getElementById('MyBtn'); var handler = function() { console.log('clicked'); } // 添加绑定事件 EventUtil.addHandler(btn, 'click', handler); 复制代码
事件冒泡应用-事件委托/代理
对“事件处理程序过多”问题的解决方案就是 事件委托 。事件委托利用了 事件冒泡 ,只指定一个事件处理程序,就可以管理一类型的所有事件。例如,click事件会一直冒泡到document层次。也就是说我们可以为整过页面(一般是某几个元素的父元素)指定一个onclick事件处理程序,而不必给每个可单击的元素分别添加事件处理程序。
<ul id="items"> <li id="item1">item1</li> <li id="item2">item2</li> <li id="item3">item3</li> </ul> 复制代码
var itemList = document.getElementById('items'); EventUtil.addHandler(itemList, 'click', function(e) { var id = e.target.id; switch(id) { case "item1": alert('item1 is clicked!'); break; case "item2": alert('item2 is clicked!'); break; case "item3": alert('item3 is clicked!'); break; case "items": alert('items is clicked!'); break; } }); 复制代码
以上所述就是小编给大家介绍的《JavaScript与HTML交互系列--事件流(冒泡、捕获、应用)和事件处理程序》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。