由事件面试题引发的总结
1、描述下js里面的【事件的三个阶段】
2、IE和W3C不同绑定事件解绑事件的方法有什么区别,参数分别是什么,以及事件对象e有什么区别
3、【事件的代理/委托】的原理以及优缺点
4、写原生js【实现事件代理】,并要求兼容浏览器
5、事件如何派发
6、JS事件模型与事件流介绍一下,事件代理用过吗?自定义事件,事件广播和分发
7、如何使用事件,以及IE和标准DOM事件模型之间存在的差别。
当面试时,问到这些问题时,问问自己是否能答的出来?
事件与事件流
不管总结什么、还是复习什么,概念从定义出发总是没错的。在《Javascript高级程序设计》中,是这么说的"事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间",也就是说事件就是js与html之间的交互实现者。
而事件流描述的是从页面中接收事件的顺序。而这儿就有两种事件流,后面详述。
事件三个阶段
可以记住这个非常了不起的demo:https://wanliyuan.github.io/blog/testFunction.html (也是网上看到的)、非常清晰得展示了三个阶段的过程。
三个阶段分别是事件捕获、处于目标阶段、事件冒泡。
DOM2级事件规定的事件流就包括这个阶段。首先先发生的是事件捕获,为截获事件提供了机会。然后就是实际的目标接收到事件,最后就是冒泡阶段,可以在这个阶段对事件作出相应。
五种事件处理程序
先来知道什么是事件处理程序。
1 2 3 4 | <input type= "button" id= "btn" value= "点击" onclick= "show(this)" > function show(obj){ console.log(obj.value); } |
这个show就是事件处理程序/事件侦听器(-----响应事件的函数)。
1、HTML事件处理程序
其实这种方式,相信前端的小伙伴们见得太多、也写的太多了。它是在HTML中定义的事件处理程序包括了执行的具体动作,或者调用页面别的地方定义的脚本,例如下面的代码:
1 2 3 4 5 6 | <input type= "button" value= "点击" onclick= "alert(11)" > <input type= "button" value= "点击" onclick= "show(this)" > function show(obj){ console.log(obj.value); } |
但是这种方式导致html与js的代码耦合度太高,也是大多数开发人员摈弃的原因。
2、DOM0级事件处理程序
通过Javascript指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。
1 2 3 4 | var btn = document.getElementById( "btn" ); btn.onclick = function(){ alert( this .id); } |
删除事件是:btn.onclick = null;
以这种方式添加的事件处理程序会在事件流的冒泡阶段被处理,并且这时候的事件处理程序是在元素的作用域中进行,里面的this就是当前元素。
3、DOM2级事件处理程序
DOM2级事件是通过addEventListener和removeEventListener来处理指定和删除事件处理程序的操作,接受三个参数(事件名、作为事件处理程序的函数,布尔值)。布尔值为true,表示在捕获阶段调用事件处理程序,若是false,则在冒泡阶段处理。
1 2 3 4 5 6 7 | var btn = document.getElementById( "myBtn" ); btn.addEventListener( "click" ,function(){ alert( this .id); }, false ); btn.addEventListener( "click" ,function(){ alert( "Hello world" ); }, false ); |
大多数情况下,都是将事件处理程序添加到事件流的冒泡阶段,这样可以最大限度地兼容各种浏览器。 通过removeEventListener删除事件处理程序时,必须使用相同参数,主要是那个函数名,必须跟addEventListener一样才有效。
1 | btn.addEventListener( "click" ,show, false ) |
1 | btn.removeEventListener( "click" ,show, false ) |
4、IE事件处理程序
IE事件处理程序跟DOM2的类似,有两个方法attachEvent和detachEvent,不过这儿的参数只有两个:事件处理程序名称与事件处理程序函数。因为IE8以及更早的版本只支持事件冒泡,所以通过attachEvent添加的事件处理程序都会被添加到冒泡阶段。
1 2 3 4 5 6 | var btn = document.getElementById( "myBtn" ); btn.attachEvent( "onclick" ,function(){ alert( "clicked" ); }); btn.attachEvent( "onclick" ,function(){ alert( "Hello world" ); }); |
IE事件处理程序跟DOM2基本的区别:第一个参数是onclick、给同一个按钮添加两个不同的事件处理程序,是以相反的顺序被触发(Hello world -> clicked)
IE事件处理程序跟DOM0基本的区别:事件处理程序的作用域是window(this==window)
通过detachEvent移除事件处理程序也一样,第二个参数要传跟attachEvent一样的名儿。
5、跨浏览器处理程序
这儿的跨浏览器的处理程序,其实就是融合了DOM2级和IE的事件处理程序,以做到兼容大多数浏览器,而且只关注冒泡阶段。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | var eventUtil = { addHandler: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 ; } } } |
三种事件对象
事件对象就是在触发dom上事件时产生的,它包含所有与事件有关的信息,如下图。所有浏览器都支持event对象,但是支持方式不同,所有才有三种事件对象。
1、DOM中的事件对象
兼容DOM的浏览器都会将一个event对象传入到事件处理程序中,无论是DOM0级还是DOM2级,都会传入event对象中。
2、IE中的事件对象
与访问DOM中的event对象不同,访问IE的event有几种方式,取决于指定事件处理程序的方法。
若是DOM0级添加事件处理程序时,event对象作为window对象的一个属性存在。
1 2 3 4 5 | var btn = document.getElementById( "myBtn" ); btn.onclick = function(){ var event = window. event ; alert( event .type); } |
若是使用attachEvent添加,就会有event对象作为参数被传入事件处理程序函数。不过也可以通过window对象来访问event对象,跟DOM0级一样。
1 2 3 4 | var btn = document.getElementById( "myBtn" ); btn.attachEvent( "click" ,function( event ){ alert( event .type); }); |
若是HTML指定事件处理程序,也还可以通过event变量访问event对象(跟DOM0级的事件模型一样)
1 | <input type= "button" value= "点击" onclick= "alert(event.type)" > |
3、跨浏览器的事件对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | var EventUtil = { addHandler:function(element,type,handler){ //省略 }, getEvent: function ( event ) { return event ? event : window. event ; }, getTarget:function( event ){ return event .target || event .srcElement; }, preventDefault:function(evnt){ if ( event .preventDefault){ event .preventDefault(); } else { event .returnValue = false ; } }, removeHandler:function(element,type,handler){ //省略 }, stopPropagation:function( event ) { if ( event .stopPropagation){ event .stopPropagation; } else { event .cancelBubble = true ; } } } |
事件代理/事件委托
每个函数都是对象,都会占用内存;内存中的对象越多,性能越差。事先对指定所有事件处理程序而导致的dom访问次数,会延迟整个页面的交互就绪事件。所以就有了事件委托,对“事件处理程序过多”的解决方案是事件委托。
1 2 3 4 5 6 7 8 9 10 11 12 | var item1 = document.getElementById( "goSomewhere" ); var item2 = document.getElementById( "doSomething" ); var item3 = document.getElementById( "sayHi" ); EventUtil.addHandler(item1, "click" ,function( event ){ location.href = "http://www.baidu.com" ; }); EventUtil.addHandler(item2, "click" ,function( event ){ document.title = "test" ; }); EventUtil.addHandler(item3, "click" ,function( event ){ alert( "Hi" ); }) |
这个是给三个li添加事件处理程序,通过事件委托的如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | var list = document.getElementById( "myLinks" ); EventUtil.addHandler(list, "click" ,function( event ){ event = EventUtil.getEvent( event ); var target = EventUtil.getTarget( event ); switch (target.id){ case "goSomewhere" : location.href = "http://www.baidu.com" ; break ; case "doSomething" : document.title = "test" ; break ; case "sayHi" : alert( "Hi" ); break ; } }) |
这个就只是添加了一个事件处理程序,用来处理页面上发生的某种特定类型的事件,提升了整体性能,Live Demo。
currentTarget与target
currentTarget是事件处理程序当前正在处理的那个元素,target是事件目标。
1 2 3 4 5 6 7 8 9 10 11 12 | var btn2 = document.getElementById( "btn2" ); btn2.onclick = function(e){ console.log(e.currentTarget== this ); //true console.log(e.target== this ); //true } document.body.onclick = function(e){ console.log(e.currentTarget== this ); //true console.log(e.target== this ); //false console.log(e.currentTarget===document.body); //false console.log( this ===document.body); //false console.log(e.target===btn2); //false } |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
2014-07-29 js瀑布流布局