JavaScript事件理解
1、事件流
当单击页面上的一个按钮时,实际上也相当于单击了按钮的button的父容器,也单击了body,甚至也单击了整个页面。
事件流描述了从页面接受事件的顺序。
事件流有两种:事件冒泡、事件捕获
例:
<html> <head></head> <body> <div> <button>Click</button> </div> </body> </html>
事件冒泡:button->div->body->html->document
事件捕获:document->html->body->div->button
一般现在浏览器都使用事件冒泡,有特殊需求时用事件捕获。
2、DOM事件流
事件流包括三个阶段:事件捕获阶段、目标作用阶段、事件冒泡阶段
事件捕获阶段:document->html->body->div
目标作用阶段:button.onclick = ...
事件冒泡阶段:div->body->html->document
3、事件处理程序
DOM0级事件处理:
为element绑定事件:element.onclick = function(){...}
为element取消绑定:element.onclick = null;
DOM2级事件处理:
为element添加事件(其中第一个参数为事件名,第二个参数为触发事件,第三个参数为true代表事件捕获阶段触发,false代表事件冒泡阶段触发):
element.addEventListener("click",myFunc1,false);
element.addEventListener("click",myFunc2,false); //触发顺序与添加顺序相同
为element取消事件():
element.removeEventListener("click",myFun1,false)
注意添加时间时:如果传入匿名函数function(){...}将无法删除,毕竟匿名,没名字怎么删
4、IE8及更早的版本,可以用attachEvent()与detachEvent()添加删除事件
用法element.attachEvent("onclick",myfunc1); //注意传入的第一个参数是onclick不是click,且只支持冒泡触发
兼容的实现
var EventUtil = { addHandler: function(element, type, handler){ if (element.addEventListener){ element.addEventListener(type,handler,false); } else if (element.attachEvent){ element.attachEvent(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(type,handler); } else { element["on" + type] = null; } } }
5、事件对象
出发DOM上某个事件时,会产生一个事件对象event,包含于该事件相关的信息,
包括事件绑定元素、事件类型等,与鼠标相关的事件包含鼠标的坐标的,与键盘相关的事件会包含按下按键的键码等
element.onclick = function(event){
alert(event.type); //"click"
}
element.addEventListener("click",function(event),false){
alert(event.type) //"click"
}
除了event.type,常用方法属性还有
取消事件默认行为:preventDefault()
阻止事件冒泡:stopPropagation()
事件绑定的对象:target
IE中的事件对象,不需要传入event对象,event对象是window的一个属性
element.onclick = function(){
var event = window.event;
alert(event.type); //"click"
}
取消事件默认行为:returnValue = faslse;
阻止事件冒泡:cancelBubble = true;
事件绑定的对象:srcElement
兼容的实现:
var EventUtil = { addHandler: function(element, type, handler){ if (element.addEventListener){ element.addEventListener(type,handler,false); } else if (element.attachEvent){ element.attachEvent(type,handler); } else { element["on" + type] = handler; } }, getEvent: function(event){ return event ? event : window.event; }, getTarget: function(event){ return event.target || event.srcElement; }, preventDefault: function(event){ if (event.preventDefault){ event.preventDefault(); } else{ event.returnValue = false; } }, removeHandler: function(element, type, handler){ if (element.removeEventListener){ element.removeEventListener(type,handler,false); } else if (element.detachEvent){ element.detachEvent(type,handler); } else { element["on" + type] = null; } }, stopPropagation: function(event){ if (event.stopPropagation){ event.stopPropagation(); } else{ event.cancelBubble = true; } } }
6、性能与内存
函数就是对象,绑定越多的事件处理函数,内存开销越大,同时访问DOM的次数变多,页面交互事件延长
事件委托:
例:
<ul id="myUl"> <li id="myLi1">新闻</li> <li id="myLi2">体育</li> <li id="myLi3">财经</li> </ul>
为上述三个列表项添加单击事件,常规的做法是为每一个添加一个单击事件,显然这是低效的
使用事件委托,在DOM数中尽可能高的层次添加时间处理程序,识别到底是哪个子元素是当前操作的元素,
并触发它的事件。
var myUL = document.getElementById("myUl"); EventUtil.addEvent(myUL,"click",function(event){ var event = EventUtil.getEvent(event); var target = EventUtil.getTarget(event); switch(target.id){ case "myLi1": //do something break; case "myLi2": //do something break; case "myLi3": //do something break; } }) ;