参考javascript编程全解 javascript高级程序设计 javascript经典实例
对事件的处理方式称为事件处理程序或事件侦听器 ,对于一个元素或事件,只能设定1个事件处理程序,却可以同时设定多个事件监听器
1)设定为HTML元素的属性
<!DOCTYPE html>
<meta charset="UTF-8" />
<head></head>
<body>
<a href="http://www.baidu.com" onclick="console.log('haha');return false;">百度</a> </body>
上面将事件处理程序设定为html元素的属性,能在元素在载入的时候被设定
2)为了在页面在中使javascript代码与html分离,可以在在javascript中将事件处理程序设定为DOM元素的属性
<a href="http://www.baidu.com" id="btn">百度</a> <script type="text/javascript">
var button = document.getElementById("btn");
function test() {
console.log('haha');
return false;
} btn.onclick = test; </script> //上面这两种方案 通过将事件处理函数设为html元素的属性 和 element.onXXX = handler 的方式 都是默认冒泡的
2016 5 9 DOM0 级别的事件处理程序只能绑定一个
3)上面的两种方式中,对一个事件只能指定一种操作(覆盖),为了实现在一个事件响应多种操作,可以使用addEventListener()方法
addEventListener(1,2,3)
参数1 事件
参数2 处理函数
参数3 true/false 指定是在捕获阶段还是冒泡阶段执行 省略第三个参数默认冒泡阶段执行(false冒泡阶段执行 true捕获阶段执行)
<button id="btn">big button</button> <script type="text/javascript">
var button = document.getElementById("btn"); button.addEventListener('click',function(){console.log('haha');},false);
button.addEventListener('click',function(){console.log('hello')},false); </script>
这里为button的click事件注册了两个事件侦听器,并且事件侦听器的触发顺序和注册的顺序相同
还可以将含有handleEvent()方法的对象指定为事件监听器
var button = document.getElementById("btn"); var listener = {
message: 'hello world', handleEvent:function(e) {
console.log(this.message);
}
}; button.addEventListener('click',listener,false);
事件的触发
上面的图是DOM的事件流 其实它是融合了两种事件流 IE的事件冒泡和事件捕获
事件冒泡:事件开始由最具体的元素接收,逐级的向上传递到父节点
事件捕获:由不具体的节点(父节点)接收事件,具体的节点最后接收事件
一个完成的DOM事件流是这样的:在捕获阶段事件由父元素向目标元素传播事件,此时目标元素没有接收到事件,在目标阶段元素接收到事件然后在冒泡阶段事件逐级的向上传递
<body id='haha'>
<button id="btn">big button</button>
<script type="text/javascript">
var btn = document.getElementById('btn'); btn.onclick = function(event) {
console.log(event.eventPhase);
};//2 目标阶段 document.body.addEventListener('click',function(event){console.log(event.eventPhase,event.target.id,event.currentTarget.id);},true);
//1 btn haha 捕获阶段 event.target指向实际发生事件的目标 event.currentTarget指向实际处理事件的目标 document.body.onclick = function(event) {console.log(event.eventPhase)}; // 3 冒泡阶段 </script>
</body>
上面的例子详细的说明的DOM事件流的执行过程(event的eventPhase属性能显示出事件处于事件流的哪个阶段 1代表捕获阶段2目标阶段 3 冒泡阶段) 先是捕获阶段处理 显示1 btn haha 接着是目标阶段处理 显示2 接着是冒泡阶段 显示3
<body id='haha'>
<button id="btn">big button</button>
<script type="text/javascript">
var btn = document.getElementById('btn'); btn.onclick = function(event) {
console.log(event.eventPhase);
event.stopPropagation();
}; document.body.addEventListener('click',function(event){console.log(event.eventPhase,event.target.id,event.currentTarget.id);},true); document.body.onclick = function(event) {console.log(event.eventPhase)}; </script>
我在上面相同的代码中在目标阶段的处理函数中加了句event.stopPropagation() 它能立即停止事件在DOM中的传播,取消进一步的捕获或者冒泡
上面的输出是 1 btn hah 2 应为它阻止了事件在冒泡阶段的传播,所以在冒泡阶段的处理函数没有执行
在DOM level3中还引入了另一个方法 stopImmediatePropagation()
它还能阻止当前侦听器的其他侦听器的执行
<body id='haha'>
<button id="btn">big button</button>
<script type="text/javascript">
var btn = document.getElementById('btn'); function hello(event) {
console.log('hello');
event.stopImmediatePropagation(); }
function world(event) {
console.log('world');
} document.body.onclick = function() {
console.log('body');
} btn.addEventListener('click',hello,false);
btn.addEventListener('click',world,false); </script>
在上面的例子中你将两个事件侦听器的注册顺序改变会发现输出不同的结果,上面的例子中只输出了hello 并且在冒泡阶段body上的处理函数也没有执行
合理的利用冒泡,可以实现事件委托 如下
<body>
<ul id="container">
<li>a</li>
<li>b</li>
<li>c</li>
<li>d</li>
<li>e</li>
</ul>
</body>
这样的页面结构中我们想单击li时获得li的内容,如果为每一个li都绑定事件侦听器的话,会增加对内存的占用,这时候我们就可以利用冒泡,来实现我们的功能
<ul id="container">
<li>a</li>
<li>b</li>
<li>c</li>
<li>d</li>
<li>e</li>
</ul> <script type="text/javascript">
var container = document.getElementById('container');
container.addEventListener('click',function(event){console.log(event.target.innerHTML);},false);
</script>
跨浏览器的通用的事件处理函数
function listenEvent(eventTarget,eventType,eventHandler) {
if(eventTarget.addEventListener) {//DOM level 2
eventTarget.addEventListener(eventType,eventHandler,false);
} else if(eventTarget.attachEvent) {//IE
eventType = "on" + eventType;
eventTarget.attachEvent(eventType,eventHandler);
} else {//DOM level 0
eventTarget["on" + eventType] = eventHandler;
}
}
跨浏览器的通用的停止事件监听函数
function removeListenEvent(eventTarget,eventType,eventHandler) {
if(eventTarget.removeEventListener) {//DOM level 2
eventTarget.removeEventListener(eventType,eventHandler,false);
} else if(eventTarget.detachEvent) {//IE /2016 3 23 改/
eventType = "on" + eventType;
eventTarget.detachEvent(eventType,eventHandler);
} else {//DOM level 0
eventTarget["on" + eventType] = null;
}
}
2016 5 9 封装更好的事件处理程序模块
var EventUtil = {
addHanler:function(element,type,handler){
if(element.addEventListener) {
element.addEventListener(type,handler,false);
} else if(element.attachEvent) {
element.attachEvent("on"+type,handler);
} else {
element["on"+type] = handler;
}
},
removeHandler:function(element,type,handler){
if(element.removeEventListener) {
element.removeEventListener(type,handler,false);
} else if(element.detachEvent) {
element.detachEvent("on"+type,handler);
} else {
element["on"+type] = null;
}
},
/*兼容IE 在IE中DOM0方式绑定事件的时候 获取event对象是window的一个属性 通过attachEvent绑定事件的时候,既可以通过event直接获取事件对象
又可以通过window.event获取事件对象*/
getEvent:function(event) {
return event ? event : window.event;
},
/*当在DOM2的时候 event.target一定指向事件发生的对象 但是在DOM0的绑定事件的时候 window.event.srcElement是this*/
getTarget:function(event){
return event.target || event.srcElement;
},
/*returnValue属性相当于preventDefault()方法*/
preventDefault:function(event) {
if(event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
},
stopPropagation:function(event){
if(event.stopPropagation) {
event.stopPropagation();
} else {
event.cancelEvent = true;
}
}
}