js事件详解
事件流
DOM事件流的三个阶段:1、事件捕获阶段 2、处于目标阶段 3、事件冒泡阶段
要理解事件流我们先来看一看事件的整个传播过程:
事件捕获阶段:用户触发事件window-->document-->html-->body-->祖先节点-->.......-->父节点-->目标元素
事件冒泡阶段:目标元素-->父节点-->......->祖先节点-->body-->html-->document-->window
所有的浏览器都实现了事件冒泡(网景除外)。
IE9+和所有的现代浏览器也都支持事件捕获并且是从window开始捕获的。
NOTE:由于低版本IE(IE8-)不能实现事件捕获,因此很少用。
事件处理程序
响应某个事件的处理函数就是事件处理程序。
1、html事件处理程序
1 <input type="button" value="单击事件" oncick="fnHandler()" />
NOTE:在html事件处理程序中使用双引号、大于、小于等符号时可以使用实体符代替。
1 <button onclick="fnShow("hello")">单击事件</button> 2 <script> 3 function fnShow(sMsg){ 4 alert(sMsg); 5 } 6 </script>
缺点:
1、时差问题。当事件处理程序代码放在元素的后面,在事件处理程序没有解析完成用户就出发了事件就会报错;
2、html代码和javascript代码耦合比较高。
2、DOM0级事件处理程序
将函数赋值给元素的事件处理程序属性
1 var oBtn = document.getElementById('btn'); 2 oBtn.onclick = function(){ 3 alert('clicked'); 4 }
删除DOM0级事件处理程序,就是将该事件处理属性设置为null。
1 var oBtn = document.getElementById('btn'); 2 oBtn.onclick = function(){ 3 alert('clicked'); 4 } 5 oBtn.onclick = null;
3、DOM2级事件处理程序
DOM2级事件处理程序提供了两个方法:addEventListener和removeEventListener。他们包含三个参数:事件名、事件处理函数、布尔值(true捕获、false冒泡).
1 var oBtn = document.getElementById('btn'); 2 oBtn.addEventListener('click', function(){ 3 alert('clicked'); 4 }, false);
好处:可以给同一个事件添加多个事件处理函数。
1 var oBtn = document.getElementById('btn'); 2 oBtn.addEventListener('click', function(){ 3 alert('clicked'); 4 }, false); 5 oBtn.addEventListener('click', function(){ 6 alert('clicking'); 7 }, false);
通过addEventListener添加的事件处理程序只能通过removeEventListener来删除。
NOTE:通过addEventListener添加的匿名函数不能被reomveEventListener删除。
1 // 添加的和删除的不是同一个函数。在堆内存中不一样。 2 var oBtn = document.getElementById('btn'); 3 oBtn.addEventListener('click', function(){ 4 alert('clicked'); // 任然会执行 5 }, false); 6 oBtn.removeEventListener('click', function(){ 7 alert('clicked'); 8 }, false);
4、IE8-事件处理程序
IE8-实现了DOM类似的两个事件处理方法:attachEvent()、detachEvent()。通过这两个方法只能添加事件到冒泡阶段。
1 var oBtn = document.getElementById('btn'); 2 oBtn.attachEvent('onclick', function(){ // 事件名前要加on 3 alert('clicked'); 4 });
NOTE:使用attachEvent添加事件时的执行作用域是全局作用域。
1 var oBtn = document.getElementById('btn'); 2 oBtn.attachEvent('onclick', function(){ 3 alert(this == window); // true 4 alert('clicked'); 5 }); 6 oBtn.addEventListener('click', function(){ 7 alert(this == window); // false 8 alert('clicked'); 9 }, false);
NOTE:在IE9、10中任然保留了这两个方法并且可以和addEventListener叠加事件,不能相互删除对应事件,IE11+已经没有这两个方法了。
1 var oBtn = document.getElementById('btn'); 2 oBtn.attachEvent('onclick', fnHandler); // 添加 3 oBtn.removeEventListener('click', fnHandler, false); // 删除 4 function fnHandler(){ 5 alert('clicked'); // 任然执行 6 } 7 oBtn.addEventListener('click', fnHandler1, false); // 添加 8 oBtn.detachEvent('onclick', fnHandler1); // 删除 9 function fnHandler1(){ 10 alert('clicked1'); // 任然执行 11 }
NOTE:attachEvent(先添加后执行)添加多个事件时与addEventListener(先添加先执行)事件执行顺序相反。在IE8-中是这样的,但是在IE9、10中却是先添加先执行。
1 var oBtn = document.getElementById('btn'); 2 oBtn.attachEvent('onclick', function(){ 3 alert('attach1'); 4 }); 5 oBtn.attachEvent('onclick', function(){ 6 alert('attach2'); 7 }); 8 9 oBtn.addEventListener('click', function(){ 10 alert('add1'); 11 }); 12 oBtn.addEventListener('click', function(){ 13 alert('add2'); 14 });
跨浏览器的事件处理程序实现
首先检查DOM2级事件处理程序、再检测IE事件处理程序、最后通过DOM0级事件处理程序。
1 var oBtn = document.getElementById('btn'); 2 var EventUtil = { 3 addHandler: function(oDom, sEventType, fnHandler){ 4 if(oDom.addEndEventListener){ 5 oDom.addEndEventListener(sEventType, fnHandler, false); 6 }else if(oDom.attachEvent){ 7 oDom.attachEvent('on' + sEventType, fnHandler); 8 }else{ 9 oDom['on' + sEventType] = fnHandler; 10 } 11 }, 12 removeHandler: function(oDom, sEventType, fnHandler){ 13 if(oDom.removeEndEventListener){ 14 oDom.removeEndEventListener(sEventType, fnHandler, false); 15 }else if(oDom.detachEvent){ 16 oDom.detachEvent('on' + sEventType, fnHandler); 17 }else{ 18 oDom['on' + sEventType] = null; 19 } 20 } 21 }
跨浏览器的事件对象
1 var EventUtil = { 2 // 绑定事件 3 addHandler: function(oDom, sEventType, fnHandler){ 4 if(oDom.addEndEventListener){ 5 oDom.addEndEventListener(sEventType, fnHandler, false); 6 }else if(oDom.attachEvent){ 7 oDom.attachEvent('on' + sEventType, fnHandler); 8 }else{ 9 oDom['on' + sEventType] = fnHandler; 10 } 11 }, 12 // 解除事件 13 removeHandler: function(oDom, sEventType, fnHandler){ 14 if(oDom.removeEndEventListener){ 15 oDom.removeEndEventListener(sEventType, fnHandler, false); 16 }else if(oDom.detachEvent){ 17 oDom.detachEvent('on' + sEventType, fnHandler); 18 }else{ 19 oDom['on' + sEventType] = null; 20 } 21 }, 22 // 获取事件对象 23 getEvent: function (event) { 24 return event ? event : window.event; 25 }, 26 // 获取事件目标元素 27 getTarget: function(event){ 28 return event.target || event.srcElement; 29 }, 30 // 阻止默认事件 31 preventDefault: function(event){ 32 if(event.preventDefault){ 33 event.preventDefault(); 34 }else{ 35 event.returnValue = false; 36 } 37 }, 38 // 阻止事件冒泡 39 stopPropagation: function(event){ 40 if(event.stopPropagation){ 41 event.stopPropagation(); 42 }else{ 43 event.cancelBubble = true; 44 } 45 } 46 }