首先要做的第一件事情就是要先搞清楚什么是事件委托/绑定,这个东西有什么用,有什么优点和缺点,这样才能更好的去了解学习相关的知识。
那就先做第一件事,认识事件绑定,不过说这个之前得先了解一下DOM事件流。
1、什么是事件流
事件流描述的是从页面中接收事件的顺序。但有意思的是,IE 和 Netscape 开发团队居然提出了差
不多是完全相反的事件流的概念。IE 的事件流是事件冒泡流,而 Netscape Communicator 的事件流是事
件捕获流。
a、IE中的事件流
IE 的事件流叫做事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深
的那个节点)接收,然后逐级向上传播到较为不具体的节点(也可以说是由子到父或者说从下到上)。
如果你单击了页面中的 <div> 元素,那么这个 click 事件会按照如下顺序传播:
(1) <div>
(2) <body>
(3) <html>
(4) document
代码如下:
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>Event Bubbling Example</title> 5 </head> 6 <body> 7 <div id="myDiv">Click Me</div> 8 </body> 9 </html>
b、Netscape提出的事件流
Netscape Communicator团队提出的另一种事件流叫做事件捕获(event capturing)。事件捕获的思想
是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于在
事件到达预定目标之前捕获它。
如果仍以前面的 HTML 页面作为演示事件捕获的例子,那么单击 <div>元素就会以下列顺序触发 click 事件。
(1) document
(2) <html>
(3) <body>
(4) <div>
“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。首
先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标接收到事件。最后一个阶段是冒泡阶
段,可以在这个阶段对事件做出响应。
再贴一个很形象的图片:
2、事件处理程序
a、HTML事件处理程序
某个元素支持的每种事件,都可以使用一个与相应事件处理程序同名的 HTML 特性来指定。
b、 DOM0 级事件处理程序
通过 JavaScript 指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性
c、DOM2 级事件处理程序
DOM2级事件定义了两个方法,用于处理指定和删除事件处理程序的操作: addEventListener()和 removeEventListener()
3、什么是事件委托/绑定
对“事件处理程序过多”问题的解决方案就是事件委托。事件委托利用了事件冒泡,只指定一个事
件处理程序,就可以管理某一类型的所有事件。例如, click 事件会一直冒泡到 document 层次。也就
是说,我们可以为整个页面指定一个 onclick 事件处理程序,而不必给每个可单击的元素分别添加事
件处理程序。
那事件委托有什么用呢?
//1.事件未绑定第一个问题 事件覆盖问题
//没有加事件绑定时出现的问题
<div style="height:100px; 100px; background="red"; ">测试</div>
1 var box = document.getElementsByTagName('div')[0]; 2 box.onclick = function() { 3 alert(1); 4 }; 5 box.onclick = function() { 6 alert(2); 7 }; 8 box.onclick = function() { 9 alert(3); // 只有这一个会显示 前两个都会被覆盖掉 10 };
//有加事件绑定时
1 box.addEventListener("click", function() { 2 alert(1) //这里的每一个都会显示出来 3 }, false) 4 box.addEventListener("click", function() { 5 alert(2) //这里的每一个都会显示出来 6 }, false) 7 box.addEventListener("click", function() { 8 alert(3) //这里的每一个都会显示出来 9 }, false)
//2.事件绑定第二个问题 相同函数屏蔽问题
1 box.addEventListener("click",fn, false) 2 box.addEventListener("click",fn, false) 3 function fn(){ 4 alert(1) 5 }
//3.事件绑定第三个问题 传递this问题
//没有事件绑定时的this
1 box.onclick = function() { 2 fn(); 3 }; 4 5 function fn() { 6 alert(this); //window 这里的this指的还是window 7 }; 8 9 //有事件绑定时的this 10 box.addEventListener('click', fn, false); 11 12 function fn() { 13 alert(this); //div this的值从上面传过来了 14 };
//4.事件绑定第三个问题 添加一个事件会被覆盖或者只执行一次
1 box.onclick = function() { 2 alert(100); 3 }; 4 box.onclick = fn; 5 6 function fn() { 7 alert(200); 8 }; 9 10 box.addEventListener("click", function() { 11 alert(100); 12 }, false) 13 box.addEventListener("click", fn, false) 14 15 function fn() { 16 alert(200); 17 };
//IE中 event对象的获取
1 box.onclick=function(ev){ 2 alert(ev) //传统方法IE无法通过参数获取ev 3 } 4 box.addEventListener("click",function(ev){ 5 alert(ev) //可以通过事件绑定来获取 6 })
最后就是兼容封装
1 //兼容写法封装 2 //添加事件绑定 3 function addEvent(obj, event, fn, bool) { //obj对象 event:事件 fn绑定函数 bool:是否捕获 4 if (obj.addEventListener) { 5 obj.addEventListener(event, fn, bool); 6 } else { 7 obj.attachEvent('on' + event, fn) 8 } 9 } 10 11 //删除事件绑定 12 function removeEvent(obj, event, fn, bool) { //obj对象 event:事件 fn绑定函数 13 if (obj.removeEventListener) { 14 obj.removeEventListener(event, fn, bool); 15 } else { 16 obj.detachEvent('on' + event, fn) 17 } 18 }
window.onload = function(){ var oUl = document.getElementById("ul1"); oUl.onclick = function(ev){ var ev = ev || window.event; //event兼容写法 var target = ev.target || ev.srcElement; //target兼容写法 if(target.nodeName.toLowerCase() == 'li'){ alert(target.innerHTML); } } }
更加详细的去参考高级程序设计第13章