重探浏览器事件(浅析事件编程化)

前言在平常开发过程中,就算不使用现在主流的框架也至少得使用个Jquery,这些工具帮我们统一不同浏览器平台之间的差异和细节,可以将注意力集中到开发上来.不过有意思的一点是,在看完高程的N年后我居然连event对象中的target和currentTarget属性的区别都忘记了.先提几个引子:你能说出event.currentTarget和event.target的区别吗?如果可以那么event.srcElement和事件监听函数中的this呢如何使用编程的方式来触发事件,而不借助浏览器默认触发方式?如何创建一个我们自己的Event对象,然后自定义我们的事件?实现上方的内容的同时该如何兼容IE浏览器?如果这几个内容你都熟悉了,那么这篇文章不会给你带来太多的帮助.在正文开始之前先来浏览一个表格,来看一下不同浏览器之间Event对象的属性有何不同:<button id=“button”>click me then change word</button> var button = document.getElementById(‘button’); button.addEventListener(‘click’,function (event) { console.log(event); });在下方的表格中我们记录了不同浏览器之间click点击后event可用的属性列表(删除了控制台输出的原型和函数引用):firefox67chrome72edge44.17763.1.0ie11ie9altKeyaltKeyaltKeyaltKeyaltKeybubblesbubblesbubblesAT_TARGETAT_TARGETbuttonbuttonbuttonbubblesbubblesbuttonsbuttonsbuttonsBUBBLING_PHASEBUBBLING_PHASEcancelBubblecancelBubblecancelablebuttonbuttoncancelablecancelablecancelBubblebuttonsbuttonsclientXclientXclientXcancelablecancelableclientYclientYclientYcancelBubblecancelBubblecomposedcomposedctrlKeyCAPTURING_PHASECAPTURING_PHASEctrlKeyctrlKeycurrentTargetclientXclientXcurrentTargetcurrentTargetdefaultPreventedclientYclientYdefaultPreventeddefaultPreventeddetailconstructorconstructordetaildetaileventPhasectrlKeyctrlKeyeventPhaseeventPhasefromElementcurrentTargetcurrentTargetexplicitOriginalTargetfromElementheightdefaultPreventeddefaultPreventedisTrustedisTrustedisPrimarydetaildetaillayerXlayerXisTrusteddeviceSessionIdeventPhaselayerYlayerYlayerXeventPhasefromElementmetaKeymetaKeylayerYfromElementisTrustedmovementXmovementXmetaKeyheightlayerXmovementYmovementYmovementXhwTimestamplayerYmozInputSourceoffsetXmovementYisPrimarymetaKeymozPressureoffsetYoffsetXisTrustedoffsetXoffsetXpageXoffsetYlayerXoffsetYoffsetYpageYpageXlayerYpageXoriginalTargetpathpageYmetaKeypageYpageXrelatedTargetpointerIdoffsetXrelatedTargetpageYreturnValuepointerTypeoffsetYscreenXrangeOffsetscreenXpressurepageXscreenYrangeParentscreenYrelatedTargetpageYshiftKeyregionshiftKeyreturnValuepointerIdsrcElementrelatedTargetsourceCapabilitiesscreenXpointerTypetargetreturnValuesrcElementscreenYpressuretimeStampscreenXtargetshiftKeyrelatedTargettoElementscreenYtimeStampsrcElementrotationtypeshiftKeytoElementtargetscreenXviewsrcElementtypetiltXscreenYwhichtargetviewtiltYshiftKeyxtimeStampwhichtimeStampsrcElementytypextoElementtarget viewytwisttiltX which typetiltY x viewtimeStamp y whichtoElement widthtype xview ywhich width x y 通过这个表格我们可以观察Event对象在不同浏览器之间结构是不同的,出人意料的是即使是在现代浏览器中事件对象也存在着差异.当然这篇文章可不是将所有的Event属性都将一遍,要知道不同的事件的Event对象结构是不同的.吐槽:本来是打算提供IE8的但是ie8不能使用addEventListener来监听事件懒得去搞ie那套数据了.currentTarget,target,srcElement,thiscurrentTarget一句话:哪个元素上监听的事件,event.currentTarget返回的就是这个对象的本身的引用.如果你的一个事件监听函数被注册到了多个DOM元素上,利用这个属性你就可以判断是谁触发的事件.this回调函数中的this === event.currentTarget.button.addEventListener(‘click’,function (event) { console.log(event.currentTarget === this); // true});targetevent.target和上面的三者不同,这里面涉及到了DOM中的一个基本知识事件冒泡和事件拦截.关于这两点我相信大家都已经了解了,即使不了解网上介绍的文章也有一大堆.我们用事件冒泡来举例,并且改写我们之前的那个例子: <div id=“wrap”> <button id=“button”>test click</button> </div> var wrap = document.getElementById(‘wrap’), button = document.getElementById(‘button’); // 注意我们监听的是wrap的click事件,而不是button的click事件 wrap.addEventListener(‘click’,function (event) { // event.target指向的是按钮,因为我们点击的是按钮 console.log(event.target === button && event.target === event.srcElement); // true // 当我们点击按钮触发的事件冒泡到了wrap,所以触发了wrap的click事件, // 此时currentTarget指向的是wrap console.log(wrap===this && wrap === event.currentTarget); // true // 直接打印event然后控制台中查看currentTaget会返回null // 你可以将他赋值到一个变量在打印输出这个变量 // see https://github.com/vuejs/vue/issues/6867#issuecomment-338195468 })在这个例子中,我们点击页面中的按钮,然后再按钮的包裹div中接收到了button冒泡上来的事件,这其中:this 和 currentTarget指向的都是添加了监听器的对象这里就是wraptarget 和 srcElement指向的是触发了事件的元素事件委托也是event.target最常见的用途之一:// Make a listvar ul = document.createElement(‘ul’);document.body.appendChild(ul);var li1 = document.createElement(’li’);var li2 = document.createElement(’li’);ul.appendChild(li1);ul.appendChild(li2);function hide(e){ // e.target 引用着 <li> 元素 // 不像 e.currentTarget 引用着其父级的 <ul> 元素. e.target.style.visibility = ‘hidden’;}// 添加监听事件到列表,当每个 <li> 被点击的时候都会触发。ul.addEventListener(‘click’, hide, false);https://developer.mozilla.org…srcElement简单理解event.srcElement === event.target.Event.srcElement 是标准的 Event.target 属性的一个别名。它只对老版本的IE浏览器有效。https://developer.mozilla.org…参考之前的表格后看来这个属性还没有被干掉,在目前最新的浏览器上它依然存在,不过已经不建议使用,除非你需要向下兼容.完整的事件编程EventTarget接口当我们在使用如下的方法的时候:elem.addEventListenerelem.removeEventListenerelem.dispatchEvent实际上是在使用EventTarget接口上的功能.例如我们可以创建一个新的EventTarget对象来添加事件监听: const a = new EventTarget; a.addEventListener(‘click’,()=>{ })但是这没有任何意义,因为这里没有事件被触发.我们仅仅是添加了事件监听器而已.为了达到我们目的通过编程的方式来执行完整的事件流程我们还需要完成如下的几步:继承EventTarget而不是直接使用EventTarget的实例,在事件监听函数中传递Event对象找个地方来触发这个事件首先我们来继承EventTarget对象:继承EventTarget在浏览器中大部分可以添加删除事件的对象都继承了EventTarget对象.你可以在控制台选择一个HTML元素一路查找原型链得到.但是他们进过重重继承,都有自己的独特属性和事件类型甚至是不同的构造函数.为了和已有的事件进行区分我们这里需要对EventTarget进行继承: // — 包装EventTarget开始 function MyEventTarget() { var target = 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; // — 包装EventTarget结束 // — 创建我们继承EventTarget的构造函数 function myElem() { } myElem.prototype = new MyEventTarget; myElem.prototype.constructor = myElem; // 创建实例 const instance = new myElem();instance.addEventListener(‘click’,()=>{ // 现在我们实例可以监听事件了}); console.log(instance);继承的过程看似非常复杂,尤其是包装EventTarget显得多此一举.但是搞定EventTarget的继承确实花了我大量的时间去寻找解决方案.你完全可以编写自己的继承方式来去继承EventTarget,不过你会发现这其中的坑非常深.简单来说,EventTarget在JavaScript中真的就是一个接口,虽然是以函数的形式存在,但是它不是构造函数(这点在Chrome64 和firefox59后进行了修改).总之通过原型链继承的EventTarget统统无法工作,如果使用ES6的类式继承在现代浏览器中(Chrome64和firefox59后)都可以进行继承.详细参考:https://stackoverflow.com/que…创建我们的Event对象获取一个event:document.getElementById(‘button’).addEventListener(‘click’,(event)=>{ // event console.log(event); })如果你在浏览器中运行这段代码并且在控制台中查看,你会发现变量event的名称MouseEvent,如果你沿着原型链向上你会发现继承的是UIEvent再次向上查看则是真正的Event.事件触发中传递的第一个参数我们通常叫它event,所有的event对象都基于Event,但是这不意味着这种关系的窗户纸就只有一层,click事件中的event和Event之间就隔着一个UIEvent.通常随着event继承的层数越多,event对象身上的属性也会越来越多.现在我们来创建一个标准的Event对象:// 使用全局的Eventnew Event(’test’,{ // 事件类型 bubbles:false, // 是否冒泡 默认false cancelable:false,// 是否可以被取消 默认false });https://developer.mozilla.org…如果你在浏览器中观察这个对象,你会发现事件上常见的属性诸如:event.targetevent.currentTargetevent.preventDefault()都在这个new Event()返回的对象中,由于其他类型的事件都继承自Event这也解释了为什么事件对象中总是有这些属性.和继承EventTarget一样,使用Event的过程也同样艰难,总的来说使用Event的难点在于它有两套API:第一套比较新的API提供了现代的接口,也就是之前例子中的方式.在创建一个已有的事件的时候,你只需要使用全局的构造函数就可以,例如:new MouseEvent(’test’,/对应MouseEvent的参数选项/),但是缺点就是不支持IE浏览器.第二套API支持IE浏览器,但是使用过程比较繁琐使用Event.createEvent(/事件类型/)创建对应事件类型的Event对象,使用Event.initEvent()来初始化事件,并且提供对应事件类型的参数,如果你创建一个MouseEvent类型的事件InitEvent方法最多需要15个参数.这种情况下使用new MouseEvent()传入对象配置的形式就简单多了.一篇值得参考的文章,使用createEvent apihttps://www.cnblogs.com/ggz19…此外不同种类的事件,都有自己的全局构造函数,不同类型的构造函数的第二个参数中的选项也是不同的.其他的构造函数请参考这里.触发我们的事件触发事件就显得简单多了,我们需要使用EventTarget.dispatchEvent方法.在我们之前创建的实例上进行事件的触发: function MyEventTarget() { var target = 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; function myElem() { } myElem.prototype = new MyEventTarget; myElem.prototype.constructor = myElem; const instance = new myElem(); instance.addEventListener(’test’, (event) => { console.log(event); // 监听事件并且打印实例 }); const myEvent = new Event(’test’); // 创建Event实例 instance.dispatchEvent(myEvent); // 触发事件当你调用dispatchEvent的时候,EventTarget会按照对应事件注册的顺序来同步执行这些事件监听器.如果在事件监听器中调用了event.preventDefault,那么dispatchEvent就返回false反之返回true(前提是cancleable为true).详细参考https://developer.mozilla.org…编程式的事件触发我们在页面中来一次具体的实战,首先建立如下的HTML结构:<div id=“wrap”> <button id=“button”>test click</button></div>我们在#wrap中监听click事件,然后在#button触发click事件.这样我们可以练习一下Event中bubbles(允许冒泡)参数的使用,另外还可以测试click事件中的Event对象如果不是MouseEvent的实例那么监听器是否会被触发. const button = document.getElementById(‘button’), wrap = document.getElementById(‘wrap’); wrap.addEventListener(‘click’, (event) => { console.log(event); // 打印event对象 }); const myEvent1 = new Event(‘click’, { bubbles: false, // 不可以冒泡 }); const myEvent2 = new Event(‘click’, { bubbles: true, // 可以冒泡 }); button.dispatchEvent(myEvent1); // 这次没有打印出内容 button.dispatchEvent(myEvent2); // 这次打印出了内容结论很明确:dispatchEvent执行的时候只要是Event的实例且类型相同那么监听器就会被触发.bubbles参数可以控制该事件是否允许冒泡 ...

March 30, 2019 · 2 min · jiezi

javascript事件基础知识

什么是事件javascript与HTML之间交互就是通过事件实现的,事件就是文档或浏览器窗口中发生的一些特定的交互瞬间。事件在浏览器中是以对象的形式存在的,即event,触发一个事件,就会产生一个事件对象event,该对象包含着所有与事件有关的信息,包括导致事件的元素、事件的类型以及其他与特定事件相关的信息。DOM元素添加事件1.DOM元素事件属性<button onclick=“clickFun()">addEvent</button>2.javascript脚本添加事件a. element.onclick=function(e){} b. element.addEventListener(event,function(e),useCapture)适用范围:现代浏览器(IE9+, Firefox , chorme, safari, opera) 参数介绍:event 必须,字符串,事件名称。(注: 不要是 on+‘someEvent’, just ‘someEvent’ is ok)。function 必须,指定事件触发时执行的函数。useCapture 可选,布尔值,指定事件是否在捕获或冒泡阶段执行,默认false(冒泡)c. element.attachEvent(event,function(e))适用范围:IE 6、7、8参数介绍:event 必须,字符串,事件名称。(注: 含“on”,比如“onclick”、“onmouseover”、“onkeydown”等)。function 必须,指定事件触发时执行的函数。栗子<button id=“myBtn”>点击这里</button><script> document.getElementById(“myBtn”).onclick=function(){clickFun()}; document.getElementById(“myBtn”).addEventListener(“click”,function(event){ clickFun(); },false); //采用事件冒泡方式给dom元素注册click事件 document.getElementById(“myBtn”).addEventListener(“click”,function(event){ clickFun(); },true); //采用事件捕获方式给dom元素注册click事件 document.getElementById(“myBtn”).attachEvent(“click”,function(event){ clickFun(); });</script>扩展:原生 Javascript 写法,跨浏览器添加事件 var EventUtil = { // 添加事件监听 add: function(element, type, callback){ if(element.addEventListener){ element.addEventListener(type, callback, false); } else if(element.attachEvent){ element.attachEvent(‘on’ + type, callback); } else { element[‘on’ + type] = callback; } } // 移除事件监听 remove: function(element, type, callback){ if(element.removeEventListener){ element.removeEventListener(type, callback, false); } else if(element.detachEvent){ element.detachEvent(‘on’ + type, callback); } else { element[‘on’ + type] = null; } } } <button id=“myBtn”>点击这里</button> <script> var myBtn = document.getElementById(“myBtn”); EventUtil.add(myBtn, ‘click’, function(){ console.log(‘被点击了’); });事件捕获、事件冒泡事件捕获事件捕获(event capturing):当鼠标点击或者触发dom事件时,浏览器会从根节点开始由外到内进行事件传播,即点击了子元素,如果父元素通过事件捕获方式注册了对应的事件的话,会先触发父元素绑定的事件。事件冒泡事件冒泡(dubbed bubbling):与事件捕获恰恰相反,事件冒泡顺序是由内到外进行事件传播,直到根节点。注:无论是事件捕获还是事件冒泡,它们都有一个共同的行为,就是事件传播阻止事件捕获/事件冒泡 event.stopPropagation() event.stopImmediatePropagation()event.stopPropagation() 会阻止事件继续分发到其他document节点,但是当前节点绑定的多个事件会继续按注册的顺序执行event.stopImmediatePropagation() 不仅阻止事件继续分发到其他document,还会将事件分发就地停止,在当前事件之后注册的其他事件,都不会执行举个栗子:此栗子是阻止事件捕获,阻止事件冒泡原理一样<div id=“div1”> <button id=“button1”>测试</button></div><script type=“application/javascript”> var div1 = document.getElementById(“div1”); var btn = document.getElementById(“button1”); div1.addEventListener(“click”, function () { alert(“div1第一次执行”); event.stopPropagation(); }, true); div1.addEventListener(“click”, function () { alert(“div1第二次执行”); }, true); btn.addEventListener(“click”, function (event) { alert(“button 执行”); }, true);</script>//执行结果 alert(“div1第一次执行”); alert(“div1第二次执行”);<div id=“div1”> <button id=“button1”>测试</button></div><script type=“application/javascript”> var div1 = document.getElementById(“div1”); var btn = document.getElementById(“button1”); div1.addEventListener(“click”, function () { alert(“div1第一次执行”); event.stopImmediatePropagation(); }, true); div1.addEventListener(“click”, function () { alert(“div1第二次执行”); }, true); btn.addEventListener(“click”, function (event) { alert(“button 执行”); }, true);</script>//执行结果 alert(“div1第一次执行”);事件代理什么是事件代理事件代理也可以称之为事件委托,事件代理就是在祖先级DOM元素绑定一个事件,当触发子孙级DOM元素的事件时,利用事件冒泡的原理来触发绑定在祖先级DOM的事件。那这是什么意思呢?网上的各位大牛们讲事件代理基本上都用了同一个例子,就是取快递来解释这个现象,借花献佛,大家认真领会一下事件代理到底是一个什么原理:有三个同事预计会在周一收到快递。为签收快递,有两种办法:一是三个人在公司门口等快递;二是委托给前台MM代为签收。现实当中,我们大都采用委托的方案。前台MM收到快递后,她会判断收件人是谁,然后按照收件人的要求签收,甚至代为付款。这种方案还有一个优势,那就是即使公司里来了新员工,前台MM也会在收到寄给新员工的快递后核实并代为签收。这里其实有2层意思的:第一,现在委托前台的同事是可以代为签收的,即程序中的现有的dom节点是有事件的;第二,新员工也是可以被前台MM代为签收的,即程序中新添加的dom节点也是有事件的。为什么要用事件代理一般来说,dom需要有事件处理程序,我们都会直接给它设事件处理程序就好了,那如果是很多的dom需要添加事件处理呢?比如我们有100个li,每个li都有相同的click点击事件,可能我们会用for循环的方法,来遍历所有的li,然后给它们添加事件,那这么做会存在什么影响呢?在JavaScript中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能,因为需要不断的与dom节点进行交互,访问dom的次数越多,引起浏览器重绘与重排的次数也就越多,就会延长整个页面的交互就绪时间,这就是为什么性能优化的主要思想之一就是减少DOM操作的原因;如果要用事件代理,就会将所有的操作放到js程序里面,与dom的操作就只需要交互一次,这样就能大大的减少与dom的交互次数,提高性能;每个函数都是一个对象,是对象就会占用内存,对象越多,内存占用率就越大,自然性能就越差了(内存不够用,是硬伤),比如上面的100个li,就要占用100个内存空间,如果是1000个,10000个呢,那只能呵呵了,如果用事件代理,那么我们就可以只对它的父级(如果只有一个父级)这一个对象进行操作,这样我们就需要一个内存空间就够了,是不是省了很多,自然性能就会更好。事件代理的实现在介绍事件代理的方法之前,先来看一段一般方法的栗子:<ul id=“ulDelegate”> <li>111</li> <li>222</li> <li>333</li> <li>444</li></ul><script> window.onload = function(){ var ulDelegate= document.getElementById(“ulDelegate”); var liDelegate= ulDelegate.getElementsByTagName(’li’); for(var i=0;i<liDelegate.length;i++){ liDelegate[i].onclick = function(){ alert(’noDelegate’); } } }</script>上面的代码很简单,我们看看有多少次的dom操作,首先要找到ul,然后遍历li,然后点击li的时候,又要找一次目标的li的位置,才能执行最后的操作,每次点击都要找一次li。用事件代理的方式来实现,上栗子:window.onload = function(){ var ulDelegate= document.getElementById(“ulDelegate”); ulDelegate.onclick = function(){ alert(‘Delegate’); }}这里用父级ul做事件处理,当li被点击时,由于冒泡原理,事件就会冒泡到ul上,因为ul上有点击事件,所以事件就会触发,当然,这里当点击ul的时候,也是会触发的。那么问题就来了,如果我想让事件代理的效果跟直接给节点的事件效果一样怎么办,比如说只有点击li才会触发,这时,Event对象就派上用场了,Event提供了一个属性叫target,可以返回事件的目标节点,我们称为事件源,也就是说,target就可以表示为当前的事件操作的dom,但是不是真正操作dom,这个是有兼容性的,标准浏览器用ev.target,IE浏览器用event.srcElement,此时只是获取了当前节点的位置,并不知道是什么节点名称,这里我们用nodeName来获取具体是什么标签名,这个返回的是一个大写的,我们可以转成小写再做比较,或者不转。栗子window.onload = function(){ var ulDelegate= document.getElementById(“ulDelegate”); ulDelegate.onclick = function(ev){ var ev = ev || window.event; var target = ev.target || ev.srcElement; if(target.nodeName.toLowerCase() == ’li’){ alert(‘Delegate’); alert(target.innerHTML); } }}这样就只有点击li会触发事件了,且每次只执行一次dom操作,如果li数量很多的话,将大大减少dom的操作,优化的性能可想而知!上面的栗子是说li操作的是同样的效果,如果每个li被点击的效果都不一样,上栗子:<ul id=“ulDelegate”> <li id=‘add’>添加</li> <li id=‘delete’>删除</li> <li id=‘edit’>修改</li></ul><script> window.onload = function(){ var ulDelegate= document.getElementById(“ulDelegate”); ulDelegate.onclick = function (ev) { var ev = ev || window.event; var target = ev.target || ev.srcElement; if(target.nodeName.toLocaleLowerCase() == ’li’){ switch(target.id){ case ‘add’ : alert(‘添加’); break; case ‘delete’ : alert(‘删除’); break; case ’edit’ : alert(‘移动’); break; } } } }</script>再来最后一个事件代理的栗子,当我们新增一个li元素的时候,同样先看一下一般写法:<ul id=“ulDelegate”> <li>111</li> <li>222</li> <li>333</li> <li>444</li></ul><button id=“addLi”>新增li</button><script> var ulDelegate= document.getElementById(“ulDelegate”); var liDelegate= ulDelegate.getElementsByTagName(’li’); var addLi = document.getElementById(“addLi”); function addClick() { for(var i=0;i<liDelegate.length;i++){ liDelegate[i].onclick = function(){ alert(’noDelegate’); } } } addClick(); addLi.onclick = function(){ var addLiElement = document.createElement(’li’); addLiElement .innerHTML = 10000Math.random(); ulDelegate.appendChild(addLiElement ); addClick(); }</script>再来看一下事件代理的写法: window.onload = function(){ var ulDelegate = document.getElementById(“ulDelegate”); var addLi = document.getElementById(“addLi”); ulDelegate.onclick = function(ev){ var ev = ev || window.event; var target = ev.target || ev.srcElement; if(target.nodeName.toLowerCase() == ’li’){ alert(‘Delegate’); alert(target.innerHTML); } }; addLi.onclick = function(){ var addLiElement = document.createElement(’li’); addLiElement .innerHTML = 10000Math.random(); ulDelegate.appendChild(addLiElement ); } }看,上面是用事件单例的方式,新添加的子元素是带有事件效果的,我们不需要去遍历元素的子节点,只需要给父级元素添加事件就好了,其他的都是在js里面的执行,这样可以大大的减少dom操作,这才是事件代理的精髓所在。总结事件是将javascript与网页联系在一起的主要方式,在使用事件时,需要考虑如下一些内存与性能方面的问题。有必要限制夜歌页面中事件处理程序的数量,数量太多会导致占用大量内存,而且也会让用户页面反应不够灵敏建立在事件冒泡机制之上的时间委托技术,可以有效的减少时间处理程序的数量建议在浏览器卸载页面之前移除页面中的所有事件处理程序事件是javascript中最重要的主题之一,深入理解事件的工作机制以及它们对性能的影响是至关重要的(总结摘自javascript高级程序设计第三版) ...

March 12, 2019 · 2 min · jiezi

Javascript 冒泡、捕获、事件代理

2019开工荒了两天,赶紧开始!为什么写事件代理、冒泡、捕获,首先冒泡和捕获是js事件的核心基础,事件代理原理来自冒泡和捕获。此文章略过标准浏览器和非标准浏览器的事件流讲解,原因很简单我们现在已经幸福了,已经不考虑IE6、7、8了直接说现代浏览器事件流,用两张图看看什么是冒泡 什么是捕获,其实从字面意思大概能看出 一个是向外一个是向内。标准事件流1、捕获阶段 (先从最外层向内查找)2、目标阶段(找到事件元(当前点击的dom))3、冒泡阶段(向上冒泡传递事件)事件冒泡<!DOCTYPE html><html lang=“en”><head> <meta charset=“UTF-8”> <meta name=“viewport” content=“width=device-width, initial-scale=1.0”> <meta http-equiv=“X-UA-Compatible” content=“ie=edge”> <title>冒泡</title></head><style> * { margin: 0; padding: 0; } div { padding: 30px; } .div1 { background: red } .div2 { background: blueviolet } .div3 { background: yellowgreen }</style><body> <div class=“div1”> 父 <div class=“div2”> 子 <div class=“div3”> 孙 </div> </div> </div> <script> // javascript事件绑定addEventListener接收三个参数, // 1、事件名称字符串且不带on // 2、回调函数 // 3、事件流方式(默认为冒泡(false),捕获为true) window.onload = function() { document.querySelector(’.div1’).addEventListener(‘click’, function() { console.log(‘点击div1’) }, false) document.querySelector(’.div2’).addEventListener(‘click’, function() { console.log(‘点击div2’) }, false) document.querySelector(’.div3’).addEventListener(‘click’, function() { console.log(‘点击div3’) }, false) } </script></body></html>先演示下冒泡事件捕获<!DOCTYPE html><html lang=“en”><head> <meta charset=“UTF-8”> <meta name=“viewport” content=“width=device-width, initial-scale=1.0”> <meta http-equiv=“X-UA-Compatible” content=“ie=edge”> <title>冒泡</title></head><style> * { margin: 0; padding: 0; } div { padding: 30px; } .div1 { background: red } .div2 { background: blueviolet } .div3 { background: yellowgreen }</style><body> <div class=“div1”> 父 <div class=“div2”> 子 <div class=“div3”> 孙 </div> </div> </div> <script> window.onload = function() { document.querySelector(’.div1’).addEventListener(‘click’, function(e) { console.log(‘点击父元素’) }, true) document.querySelector(’.div2’).addEventListener(‘click’, function(e) { console.log(‘点击子元素’) }, true) document.querySelector(’.div3’).addEventListener(‘click’, function(e) { console.log(‘点击孙子元素’) }, true) } </script></body></html>看看效果事件委托我理解的事件委托的好处有两点1、减少事件绑定次数2、可以给未知元素绑定事件(例如动态渲染的List)其原理就是利用事件冒泡向外传递的特性,下面代码解析一下:同样忽略低版本浏览器获取当前target兼容性问题<!DOCTYPE html><html lang=“en”><head> <meta charset=“UTF-8”> <meta name=“viewport” content=“width=device-width, initial-scale=1.0”> <meta http-equiv=“X-UA-Compatible” content=“ie=edge”> <title>冒泡</title></head><style> * { margin: 0; padding: 0; } div { padding: 30px; border: 1px solid red } div span { display: inline-block; padding: 10px; border: 1px solid blueviolet }</style><body> <div class=“div1”> <span>1</span> <span>2</span> <span>3</span> <span>4</span> </div> <script> window.onload = function() { document.querySelector(’.div1’).addEventListener(‘click’, function(e) { //回调函数e为事件对象,同伙事件对象可以获取当前点击dom console.log(e.target) //获取到当前事件源(dom)后再去搞一些你想搞的事情就ok了 //搞事情代码 }, false) } </script></body></html>老规矩,看看效果最后顺便说一下事件对象功能很全的,看下图能获取到很多当前dom的周边,可以搞好多其它的事情。感谢阅读,欢迎吐槽!谢谢! ...

February 13, 2019 · 2 min · jiezi