当前位置: 首页 > Web前端 > HTML5

再探浏览器事件(事件编程解析)

时间:2023-04-05 21:32:11 HTML5

前言在平时的开发过程中,即使不用现在主流的框架,至少也要用到Jquery。这些工具帮助我们统一了不同浏览器平台之间的区别和区别。细节,可以重点开发。但有趣的一点是,看了N年的海拔,我什至忘记了事件对象中target和currentTarget属性的区别。先提几个介绍:你能分清event.currentTarget和event.target的区别吗?如果是这样,事件侦听器函数中的event.srcElement和this怎么办?如何以编程方式触发事件而不是使用浏览器默认的触发方式?如何创建我们自己的Event对象,然后自定义我们的事件呢?如何在兼容IE浏览器的同时实现以上内容?如果您熟悉这些内容,那么这篇文章不会给您带来太大的帮助。正文开始前,我们先浏览一张表格,看看不同浏览器下Event对象的属性有何不同:clickmethenchangeword');button.addEventListener('click',function(event){console.log(event);});Inthetablebelow,werecordthelistofattributesavailabletotheeventafterclickingbetweendifferentbrowsers(delete了控制台输出的原型和函数引用):firefox67chrome72edge44.17763.1.0ie11ie9altKeyaltKeyaltKeyaltKeyaltKeybubblesbubblesbubblesAT_TARGETAT_TARGETbuttonbuttonbuttonbubblesbubblesbuttonsbuttonsbuttonsBUBBLING_PHASEBUBBLING_PHASEcancelBubblecancelBubblecancelablebuttonbuttoncancelablecancelablecancelBubblebuttonsbuttonsclientXclientXclientXcancelablecancelableclientYclientYclientYcancelBubblecancelBubblecomposedcomposedctrlKeyCAPTURING_PHASECAPTURING_PHASEctrlKeyctrlKeycurrentTargetclientXclientXcurrentTargetcurrentTargetdefaultPreventedclientYclientYdefaultPreventeddefaultPreventeddetailconstructorconstructordetaildetaileventPhasectrlKeyctrlKeyeventPhaseeventPhasefromElementcurrentTargetcurrentTargetexplicitOriginalTargetfromElementheightdefaultPreventeddefaultPreventedisTrustedisTrustedisPrimarydetaildetaillayerXlayerXisTrusteddeviceSessionIdeventPhaselayerYlayerYlayerXeventPhasefromElementmetaKeymetaKeylayerYfromElementisTrustedmovementXmovementXmetaKeyheightlayerXmovementYmovementYmovementXhwTimestamplayerYmozInputSourceoffsetXmovementYisPrimarymetaKeymozPressureoffsetYoffsetXisTrustedoffsetXoffsetXpageXoffsetYlayerXoffsetYoffsetYpageYpageXlayerYpageXoriginalTargetpathpageYmetaKeypageYpageXrelatedTargetpointerIdoffsetXrelatedTargetpageYreturnValuepointerTypeoffsetYscreenXrangeOffsetscreenXpressurepageXscreenYrangeParentscreenYrelatedTargetpageYshiftKeyregionshiftKeyreturnValuepointerIdsrcElementrelatedTargetsourceCapabilitiesscreenXpointerTypetargetreturnValuesrcElementscreenYpressuretimeStampscreenXtargetshiftKeyrelatedTargettoElementscreenYtimeStampsrcElementrotationtypeshiftKeytoElementtargetscreenXviewsrcElementtypetiltXscreenYwhichtargetviewtiltYshiftKeyxtimeStampwhichtimeStampsrcElementytypextoElementtargetviewytwisttiltXwhichtypetiltYxviewtimeStampywhichtoElementwidthtypexviewywhichwidthxy通过这个表格我们可以观察Event对象在不同浏览器之间结构是不同的,出人意料的是即使是在现代浏览器中事件对象也存在着差异.当然ThisarticleisnotaboutgoingthroughalltheEventattributes.YoumustknowthattheEventobjectstructureofdifferenteventsisdifferent.Tucao:IoriginallyplannedtoprovideIE8,butIE8cannotuseaddEventListenertolistentoevents.IamtoolazytogetthedataofIE.currentTarget,target,srcElement,thiscurrentTargetInaword:whichelementislisteningtotheevent,event.currentTargetreturnsthereferenceoftheobjectitself.IfoneofyoureventlisteningfunctionsisregisteredtomultipleDOMelements,usethisattribute,youcandeterminewhotriggeredtheevent.thisinthiscallbackfunction===event.currentTarget.button.addEventListener('click',function(event){console.log(event.currentTarget===this);//真的});targetevent.target与上面三个不同的是,它涉及到DOM中事件冒泡和事件拦截的一个基础知识。关于这两点我相信大家都已经明白了,就算你不明白,网上也有一大堆文章。我们以事件冒泡为例,重写我们之前的例子:),button=document.getElementById('按钮');//注意我们监听的是wrap的点击事件,不是按钮点击事件wrap.addEventListener('click',function(event){//event.target指向按钮,因为我们点击了按钮console.log(event.target===button&&event.target===event.srcElement);//true//当我们点击按钮时,事件冒泡到wrap,所以wrap的click事件被触发,//此时currentTarget指向wrapconsole.log(wrap===this&&wrap===event.currentTarget);//true//直接打印事件,在控制台查看currentTaget会返回null//你可以赋值给一个变量,打印出这个变量//见https://github.com/vuejs/vue/issues/6867#issuecomment-338195468})在这个例子中,我们点击页面上的按钮,然后接收按钮的包装div中的按钮冒泡事件,其中:this和currentTarget指向添加了监听器的对象。此处,wraptarget和srcElement指向触发事件的元素。事件委托也是event.target最常见的用途之一://创建一个列表varul=document.createElement('ul');document.body.appendChild(ul);varli1=document.createElement('li');varli2=document.createElement('li');ul.appendChild(li1);ul.appendChild(li2);functionhide(e){//e.target引用

  • 元素//不像e.currentTarget引用其父
      元素。e.target.style.visibility='hidden';}//添加监听事件到列表中,当每个
    • 被点击时,都会触发ul.addEventListener('click',hide,false);https://developer.mozilla.org...srcElement简单地理解事件。srcElement===event.target.Event.srcElement是标准Event.target属性的别名。它只适用于旧版本的IE。https://developer.mozilla.org...参考了之前的表格,好像这个属性并没有被杀掉。它仍然存在于最新的浏览器上,但不建议使用它,除非你需要向后兼容。完成事件编程EventTarget接口当我们使用以下方法时:elem.addEventListenerelem.removeEventListenerelem.dispatchEvent实际上是在使用EventTarget接口上的函数。比如我们可以新建一个EventTarget对象来添加事件监听:consta=newEventTarget;a.addEventListener('click',()=>{})但这没有任何意义,因为这里没有触发任何事件。我们刚刚添加了事件监听器。为了通过编程的方式达到我们的目的执行完整的事件流,我们需要完成以下步骤:继承EventTarget,而不是直接使用EventTarget的实例,在事件监听函数中传递Event对象,找个地方触发这个事件首先,我们继承EventTarget对象:InheritEventTargetin浏览器中大部分可以添加和删除事件的对象都继承了EventTarget对象。你可以在控制台中选择一个HTML元素,一路搜索原型链获取。但是它们已经被继承了很多次,并且有自己独特的属性和事件类型。甚至不同的构造函数。为了区别于已有的事件,这里需要继承EventTarget://---WrapEventTargettostartfunctionMyEventTarget(){vartarget=document.createTextNode(null);this.addEventListener=目标。addEventListener.bind(target);this.removeEventListener=target.removeEventListener.bind(target);this.dispatchEvent=target.dispatchEvent.bind(target);}我的事件目标et.prototype=EventTarget.prototype;//---包装EventTarget结束//---创建构造函数myElem(){}myElem.prototype=newMyEventTarget;myElem.prototype.constructor=myElem;//创建实例constinstance=newmyElem();instance.addEventListener('click',()=>{//现在我们的实例可以监听事件了});控制台日志(实例);继承的过程看起来很复杂,尤其是包装EventTarget显得多此一举。但是我花了很多时间才找到解决方法来获取EventTarget的继承。你可以自己写继承方法来继承EventTarget,但是你会发现坑很深。简单来说,Eve??ntTarget其实就是JavaScript中的一个接口。虽然以函数的形式存在,但并不是构造函数(这点在Chrome64和firefox59之后有所修改)。总之,通过原型链继承的EventTarget是行不通的。如果使用ES6类继承,在现代浏览器(Chrome64和firefox59之后)都可以使用class来继承。详细参考:https://stackoverflow.com/que...创建我们的Event对象来获取一个事件:document.getElementById('button').addEventListener('click',(event)=>{//事件控制台。log(event);})如果你在浏览器中运行这段代码并在控制台中查看,你会发现变量事件的名称MouseEvent,如果你沿着原型链往上走,你会发现继承的UIEvent才是真正的再次抬头时的事件。事件触发器中传递的第一个参数通常称为事件,所有事件对象都是基于事件的,但这并不意味着关系只有一层窗纸,点击事件中的事件和Event之间有一个UIEvent。通常,事件继承的层次越多,事件对象的属性就越多。现在我们创建一个标准事件对象//使用globalEventnewEvent('test',{//事件类型bubbles:false,//是否冒泡,默认falsecancelable:false,//是否可以取消,defaultfalse});https://developer.mozilla.org...如果你在浏览器中观察这个对象,你会发现事件上的公共属性如:event.targetevent.currentTargetevent.preventDefault()是all在这个newEvent()返回的对象中,由于其他类型的事件都是继承自Event,这也解释了为什么事件对象中总是有这些属性。和继承EventTarget一样,使用Event的过程也很艰难。总的来说,使用Event的难点在于它有两套API:第一套相对较新的API提供了现代的接口,也就是前面例子中的方式。创建已有事件时,只需要使用全局构造函数即可,例如:newMouseEvent('test',/*对应MouseEvent的参数选项*/),缺点是不支持IE浏览器。第二套API支持IE浏览器,但使用过程比较繁琐。使用Event.createEvent(/*事件类型*/)创建事件类型对应的Event对象,使用Event.initEvent()初始化事件,并提供事件类型对应的参数。如果创建类型为MouseEvent的事件,则InitEvent方法最多需要15个参数。这种情况下,使用newMouseEvent()传入对象配置的形式就简单多了。一篇值得参考的文章使用了createEventapihttps://www.cnblogs.com/ggz19...另外,不同类型的事件都有自己的全局构造函数,不同类型的构造函数第二个参数中的选项也不同。其他构造函数请参考这里。触发我们的事件触发事件就简单多了,我们需要用到EventTarget.dispatchEvent方法。在我们创建较早事件的实例上触发器:函数MyEventTarget(){vartarget=document.createTextNode(null);this.addEventListener=target.addEventListener.bind(target);this.removeEventListener=target.removeEventListener.bind(target);this.dispatchEvent=target.dispatchEvent.bind(target);}MyEventTarget.prototype=EventTarget.prototype;函数myElem(){}myElem.prototype=newMyEventTarget;myElem.prototype.constructor=myElem;constinstance=newmyElem();instance.addEventListener('test',(event)=>{console.log(event);//监听事件并打印实例});constmyEvent=newEvent('测试');//创建事件实例instance.dispatchEvent(myEvent);//触发事件当你调用dispatchEvent时,EventTarget会按照相应事件注册的顺序同步执行这些事件监听器。如果在事件监听器中调用了event.preventDefault,那么dispatchEvent将返回false,否则返回true(前提是cancleable为true)。具体可以参考https://developer.mozilla.org...程序化事件触发我们在页面上进行具体的实战。首先,创建以下HTML结构:testclick
  • 我们在#wrap中监听click事件,然后在#button中触发click事件。这样我们就可以练习Event中bubbles(允许冒泡)参数的使用,也可以测试Event对象在点击事件中如果不是MouseEvent的实例是否会触发监听器。constbutton=document.getElementById('button'),wrap=document.getElementById('wrap');wrap.addEventListener('click',(event)=>{console.log(event);//打印事件对象});constmyEvent1=newEvent('click',{bubbles:false,//不允许有气泡});constmyEvent2=newEvent('click',{bubbles:true,//canbubble});button.dispatchEvent(myEvent1);//这次没有打印内容button.dispatchEvent(myEvent2);//这次打印的内容结论很明确:当dispatchEvent被执行时只要是Event的实例并且是同类型的,就会触发监听器。bubbles参数可以控制事件是否允许冒泡