一、事件流
1.事件冒泡:事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。最早使用事件冒泡的是IE,现在绝大多数浏览器都使用冒泡。IE9、Firefox、Chrome、Safari都将事件冒泡到window。2.事件捕获:事件捕获是由不太具体的节点先接收事件,而最具体的节点最后接收事件。顺序与事件冒泡刚好相反。IE9、Safari、Chrome、Opera都支持这种事件类型。
3.DOM2级事件:DOM2规定事件流包括三个阶段,捕获阶段,为截获事件提供了机会;实际目标接收处理阶段;冒泡阶段,可以在这个阶段对事件做响应。如果在捕获阶段,上层DOM把事件拦下,那么实际DOM的事件就不能触发。具体流程见下图。
二、事件处理程序
1.DOM1级,只能通过element.οnclick=function(){}这种形式添加处理,也就是说,只能像一个事件上绑定一个处理程序。2.DOM2级,可以使用addEventListener和removeEventListener添加或删除事件处理程序。addEventListener有三个参数:事件类型、处理程序、是否在捕获阶段调用事件处理程序(默认false)。下面例子说明了第三个参数的作用。设置为true时,控制台依次输出box1到box4,说明事件处理是在捕获阶段执行的,div1先捕获到click,执行处理程序,然后div2再捕获到。设置为false时,依次输出box4到box1,即捕获阶段谁都不处理事件,最内层的div4捕获后,没有下一层了,开始处理,然后事件开始冒泡,div3开始处理。DOM2级可以在一个事件上绑定多个处理程序。removeEventListener如果第二个参数是新函数而不是之前添加的函数引用,则完全不其作用。所以如果想把事件remove掉,add的时候只能传函数引用进去。
- //小实验,事件关于addEventListener的第三个参数。
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
- <html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
- <title>addEventListener</title>
- <style>
- div{position:absolute;}
- #box1{left:50px;top:60px;height:50px;width:500px;height:500px;background-color:#00F;}
- #box2{left:50px;top:50px;height:50px;width:400px;height:400px;background-color:#09C;}
- #box3{left:50px;top:50px;height:50px;width:300px;height:300px;background-color:#393;}
- #box4{left:50px;top:50px;height:50px;width:200px;height:200px;background-color:#9C6;}
- </style>
- </head>
- <body>
- <div id="box1">
- <div id="box2">
- <div id="box3">
- <div id="box4"></div>
- </div>
- </div>
- </div>
- <script language="javascript">
- var boxes=document.getElementsByTagName("div");
- var useCapture=true;
- var clickHandler=function(){
- console.log(this.id);
- }
- for(var n=0;n<boxes.length;n++){
- boxes[n].addEventListener("click",clickHandler,useCapture);
- }
- </script>
- </body>
- </html>
三、事件对象
1.每个事件处理程序,其实都是一个回调函数,回调时默认传入一个event参数,这是一个保存了事件各种信息的对象。2.event.type表示事件的类型;event.target表示事件的对象,相当于this;这两个最为常用,尤其是在写通用事件处理程序时。
3.event.preventDefault()可以阻止事件的默认行为,比如链接在点击的时候会跳转到href标明的地址,用这个方法可以阻止这种跳转。4.event.stopPropagation()可以阻止事件在事件流中传播,下面这个例子,div3和div4的click都不会触发。
- //小实验,阻止事件传播
- var boxes=document.getElementsByTagName("div");
- var useCapture=true;
- var clickHandler=function(){
- console.log(this.id);
- if(this.id=="box2"){
- event.stopPropagation();
- }
- }
- for(var n=0;n<boxes.length;n++){
- boxes[n].addEventListener("click",clickHandler,useCapture);
- }
四、事件类型
1.一些标准的标签有load事件,表示已经加载完成,如body、image;还有一些标签以非标准形式支持load,如script。因此,可以用这个事件判断js脚本是不是动态加载完成了。2.焦点事件blur、focus与其他事件有一定差异,因为他们不会冒泡。focusin是focus的通用版本、focusout是blur的通用版本。
3.DOM3级将mouseenter和mouseleave纳入了规范,这两个事件不会冒泡,而且鼠标移动到后代元素上不会出发。jQuery很早就支持了这两个事件。相比mouseover和mouseout,这两个事件简直太有用、太方便了。
4.关于鼠标事件中的鼠标位置:clientX/Y是相对于可视区域左上角的坐标(不算滚动条);pageX/Y是相对于页面左上角的坐标(包含了滚动条);screenX/Y是相对于显示器左上角的坐标;offsetX/Y是相对与触发元素的左上角的坐标。
五、内存与性能
1.绑定过多是事件处理程序,必然会消耗内存,延迟整个页面响应时间。2.解决事件过多的问题,可以使用“事件委托”。比如所有的click最终都会冒泡的document,那么理论上只要在document绑定一个click事件,整个页面的类似事件就都能处理了。这样能减少很多内存占用。不是所有事件都能使用委托,只有冒泡的才能使用,最合适做委托的有:click、mousedown、mouseup、keydown、keyup、keypress。
- //小实验 事件委托
- document.body.addEventListener("click",function(event){
- console.log(event.target.id);
- });
六、自定义事件
1.可以通过event=document.createEvent(eventName)自定义事件类型,创建后会产生一个initEventName方法。2.通过event.initEventName()初始化事件对象。
3.通过element.dispatchEvent(event)触发事件。
- //小实验 自定义事件
- var div=document.getElementById("box1");
- div.addEventListener("mousemove",function(event){//触发自定义事件
- if(event.offsetX<10 && event.offsetY<10){
- var myevent=document.createEvent("CustomEvent");
- myevent.initCustomEvent(
- "MyEvent",//type
- true,//bubbles
- true,//cancelable
- {text:"hello"}//detail
- );
- this.dispatchEvent(myevent);
- }
- })
- div.addEventListener("MyEvent",function(event){//处理自定义事件
- console.log(event.detail.text);
- });