事件
1 事件流
-
冒泡:从该元素开始由里向外触发一类事件,事件对象中的事件源为该元素,触发同一类事件,在子级父级祖级中拿到的事件源一样,均为被触发元素;
- 阻止冒泡:
e.stopPropagation?e.stopPropagation():e.cancelBubble=true;
- 关于冒泡的运用:典型的是事件委托
- 事件委托:将事件添加到最高级元素上,事件中获取事件源的属性,当触发某元素上的事件时,就会在最高级中获取事件源,也就是该元素;
- 实例1:在多层嵌套的div中,点击哪个元素,就弹出哪个元素的class名;
- 方法:
- 1)遍历div,给每个元素添加点击事件,然后弹出class名,但是会出现冒泡事件,所以必须阻止冒泡事件
- 2)事件委托
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>冒泡与阻止冒泡</title> <style> div{ padding: 100px; font-size: 50px; } .div1{ background-color: red; } .div2{ background-color: blue; } .div3{ background-color: green; } </style> </head> <body> <div class="div1">div1 <div class="div2">div2 <div class="div3">div3</div> </div> </div> <script> //需求:点击谁,弹出谁的class名 var oDiv1=document.getElementsByTagName("div")[0]; var aDiv=document.getElementsByTagName("div"); //第一种方法:遍历div,给每个元素添加点击事件,然后弹出class名,要阻止冒泡事件 for(var i=0; i<aDiv.length; i++){ aDiv[i].onclick=function (e) { alert(this.className); //会存在冒泡事件,所以要阻止冒泡 e.stopPropagation? e.stopPropagation():e.cancelBubble=true; } } //第二种方法:事件委托,委托给最高级,通过事件源获取 //由于事件存在冒泡特性,所以在触发某元素的事件时,同时会触发其父级和祖级的该类事件,都传入同一个事件对象,里面的事件源都是触发的某元素,所以只要在最高级上添加点击事件,里面获取事件源的class名即可 oDiv1.onclick=function (e) { e=e||window.event; e.target=e.target||e.srcElement; alert(e.target.className); } </script> </body> </html>
- 方法:
- 实例2:在body元素中有多个div元素,在点击里面的a链接后,就删除该a元素的父级div;
- 方法:事件委托给最高级body元素,给body添加点击事件,然后在事件匿名函数中获取事件源,然后删除事件源的父级div;
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>事件委托</title> <style> div{ width: 100px; height: 100px; background-color: lightseagreen; float: left; margin: 5px; } div a{ color: red; text-decoration: none; font-weight: bold; } </style> </head> <body> <!--<div> <a href="javascript:void(0);">点吧</a> </div>--> <script> for(var i=0; i<20; i++){ var odiv=document.createElement("div"); odiv.innerHTML=`<a href="javascript:void(0);">${i}点吧</a>`; document.body.appendChild(odiv); } //事件委托,给最高级元素添加事件 //当点击a标签时,就会触发a的点击事件,同时由里向外触发父级div和祖级body的点击事件,然后每个事件中拿到的事件对象上的target属性值都是a元素,即事件源都是a元素; //通过判断事件源来决定删除哪个a元素的父级; document.body.onclick=function (e) { e=e||window.event; e.target=e.target || e.srcElement; if(e.target.tagName.toLowerCase()==="a"){ this.removeChild(e.target.parentNode); } } </script> </body> </html>
- 注:某个元素事件发生,不用给其添加代码,只要在它身上触发该事件就行,添加代码,只不过给其设置一个能够显现出来的动作而已,不添加,也会触发;
- 阻止冒泡:
-
onmouseenter、onmouseleave、onmouseover、onmouseout四者关系解读
- 相同点:都是添加鼠标移入移出事件
- 不同点:onmouseover和onmouseout中会存在冒泡事件;onmouseenter和onmouseleave中不存在冒泡事件;
- 实例解读:
- 需求:当鼠标移动到div元素身上时,p元素显示,显示的时候,移动到p元素身上,仍然显示,移动到div和p元素之外的位置,p元素再隐藏;
- 方法:
- 使用onmouseenter与onmouseleave设置:当光标从div上移出到p元素上时,没有打印out,也没有再次打印in,意思是两个事件都没有被触发,指的就是当移动到子级身上时,根本不算移出;所以给oDiv元素添加onmouseenter事件,指的是当光标移入到它或者是它的子级,都会触发事件;不是冒泡事件,而是将oDiv和它的子级作为了一个整体;
- 使用onmouseover与onmouseout设置:使用onmouseover和onmouseout也会实现需求,但是实现的过程不同,当光标移入div会触发onmouseover事件,然后打印in,当光标从div元素上移出并立刻进入p元素时,此时发生了两个过程,移出div时,会触发div的onmouseout事件,打印out,并使p隐藏;然后迅速进入p元素,此时触发p元素的onmouseover事件,但是没有动作,会通过冒泡事件,连带父级div触发onmouseover事件,所以再次打印in,然后p显示;所以肉眼没有发现p被隐藏,又被显示。当移出p元素时也是通过冒泡事件,将父级onmouseout事件触发,进而打印out,使p隐藏;
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>onmouseenter与onmouseleave解读</title> <style> *{ margin: 0; padding: 0; } .div1{ width: 200px; height: 50px; background-color: lightseagreen; position: relative; margin: 20px auto; } .div1 p{ width: 500px; height: 300px; position: absolute; right: 0; top: 50px; background-color: lightpink; display: none; } </style> </head> <body> <div class="div1"> <p></p> </div> <script> //需求:当鼠标移动到div元素身上时,p元素显示,显示的时候,移动到p元素身上,仍然显示,移动到div和p元素之外的位置,p元素再隐藏; var oDiv=document.getElementsByTagName("div")[0]; var oP=document.getElementsByTagName("p")[0]; //1 使用onmouseenter与onmouseleave设置 oDiv.onmouseenter=function () { console.log("in"); oP.style.display="block"; }; oDiv.onmouseleave=function () { console.log("out"); oP.style.display="none"; }; //当光标移入div,再移入p,再移出p,整个过程中打印结果:in-out; //2 使用onmouseover与onmouseout设置 oDiv.onmouseover=function () { console.log("in"); oP.style.display="block"; }; oDiv.onmouseout=function () { console.log("out"); oP.style.display="none"; } //当光标移入div,再移入p,再移出p,整个过程中打印结果:in-out-in-out </script> </body> </html>
-
冒泡的问题:给父级添加事件后,在子级上触发,还会触发父级的事件,会重复触发,造成性能不好,还会造成代码混乱;
- 解决方法:在子级中添加相同事件,并在事件中阻止冒泡;代码:
e.stopPropagation?e.stopPropagation():e.cancelBubble=true;
; - 通过关联元素获取到子级,然后判断阻止,此方法只能解决代码混乱问题,不会进行性能优化,父级事件还会因子级事件触发而触发,只不过是在父级事件中阻断了一些代码的执行,避免重复设置,造成混乱;
- 在父级事件函数中,通过事件对象获取事件源,通过事件源来筛选触发元素,不能阻止冒泡事件发生,但是可以阻止,当从子级触发时,不会再执行父级函数体内的代码;
- 解决方法:在子级中添加相同事件,并在事件中阻止冒泡;代码:
-
冒泡的问题及解决问题的实例:
- 实例1:当鼠标滑到div上时,新建一个p元素,让其跟随光标移动,当光标移出div后,p元素消失;
- 问题:当鼠标移动到子级p身上的时候,会触发鼠标事件,通过冒泡触发父级身上的鼠标事件,进而再新建一个p元素,进而出错;
- 解决方法:1)将子级p元素中的冒泡事件阻止,这样父级就不会被关联触发;2)利用对象事件上的属性获取添加事件元素的关联元素,判断当包含时,则阻止程序执行,进而不会被影响;3)利用onmouseenter和onmouseleave添加事件;
- 注意:鼠标移动事件太灵敏,所以需要在定位数值设置时使光标与定位元素有一段距离;
- 代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>实例2</title> <style> *{ margin: 0; padding: 0; } .div{ width: 500px; height: 350px; background-color: red; margin: 20px auto; position: relative; } .div p{ width: 50px; height: 50px; background-color: blue; position: absolute; } </style> </head> <body> <div class="div"></div> <script> var oDiv=document.getElementsByTagName("div")[0]; var oS=null; //方法1:将子级p元素中的冒泡事件阻止,这样父级就不会被关联触发; oDiv.onmouseover=function () { oS=document.createElement("p"); oS.onmouseover=function (e) { //阻止冒泡事件 e=e||window.event; e.stopPropagation? e.stopPropagation():e.cancelBubble=true; }; this.appendChild(oS); }; oDiv.onmousemove=function (e) { e=e||window.event; oS.style.left=e.clientX-this.offsetLeft-this.clientLeft+5+"px"; oS.style.top=e.clientY-this.offsetTop-this.clientTop+5+"px"; }; oDiv.onmouseout=function () { oS.onmouseout=function (e) { //阻止冒泡事件 e=e||window.event; e.stopPropagation? e.stopPropagation():e.cancelBubble=true; }; this.removeChild(oS); } //方法2:利用对象事件上的属性获取添加事件元素的关联元素,判断当包含时,则阻止程序执行,进而不会被影响; oDiv.onmouseover=function (e) { e=e||window.event; //关联元素的获取,在事件对象上 var oMo=e.fromElement || e.realtedTarget; //判断是否包含关联元素,包含则阻止程序执行; if(this.contains(oMo)) return; oS=document.createElement("p"); this.appendChild(oS); }; oDiv.onmousemove=function (e) { e=e||window.event; oS.style.left=e.clientX-this.offsetLeft-this.clientLeft+8+"px"; oS.style.top=e.clientY-this.offsetTop-this.clientTop+8+"px"; }; oDiv.onmouseout=function (e) { e=e||window.event; var oMo=e.toElement || e.realtedTarget; if(this.contains(oMo)) return; this.removeChild(oS); } //方法3:使用onmouseenter和onmouseleave oDiv.onmouseenter=function () { oS=document.createElement("p"); this.appendChild(oS); }; oDiv.onmousemove=function (e) { e=e||window.event; oS.style.left=e.clientX-this.offsetLeft-this.clientLeft+10+"px"; oS.style.top=e.clientY-this.offsetTop-this.clientTop+10+"px"; }; oDiv.onmouseleave=function () { this.removeChild(oS); } </script> </body> </html>
- 实例2:当鼠标移动到div元素身上时,p元素显示,显示的时候,移动到p元素身上,仍然显示,移动到div和p元素之外的位置,p元素再隐藏;
- 问题:当鼠标移动到子级p元素身上时,会触发p元素的鼠标事件,进而再次触发父级div的鼠标事件,这样就会重复触发,性能不好,所以要阻止子级的冒泡事件
- 解决方法:1)在子级中阻止冒泡事件,但是需要在子级中添加显示隐藏;2)通过对象事件获取关联元素;3)设置onmouseenter和onmouseleave事件,能够解决性能问题,事件只触发一次
- 注意:关联元素的方法可取,但是没有解决性能问题,因为滑到子级上,还是会触发父级的鼠标事件,没有完成性能优化,此处要注意的是,显示隐藏代码一定要设置在判断元素之后;在父级事件中代码执行前,判断关联元素的目的:是阻止父级事件中代码的执行,不会阻止冒泡事件的发生;
- 代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>冒泡问题解读</title> <style> *{ margin: 0; padding: 0; } .div1{ width: 200px; height: 50px; background-color: lightseagreen; position: relative; margin: 20px auto; } .div1 p{ width: 500px; height: 300px; position: absolute; right: 0; top: 50px; background-color: lightpink; display: none; } </style> </head> <body> <div class="div1"> <p></p> </div> <script> //需求:当鼠标移动到div元素身上时,p元素显示,显示的时候,移动到p元素身上,仍然显示,移动到div和p元素之外的位置,p元素再隐藏; //问题:当鼠标移动到子级p元素身上时,会触发p元素的鼠标事件,进而再次触发父级div的鼠标事件,这样就会重复触发,性能不好,所以要阻止子级的冒泡事件 var oDiv=document.getElementsByTagName("div")[0]; var oP=document.getElementsByTagName("p")[0]; //方法1:在子级中阻止冒泡事件,但是需要在子级中添加显示隐藏; oDiv.onmouseover=function () { console.log("in"); oP.style.display="block"; oP.onmouseover=function (e) { //阻止冒泡但是要重新设置显示隐藏; this.style.display="block"; e=e||window.event; //阻止子级的冒泡事件 e.stopPropagation?e.stopPropagation():e.cancelBubble=true; } }; oDiv.onmouseout=function () { console.log("out"); oP.style.display="none"; oP.onmouseout=function (e) { this.style.display="none"; e=e||window.event; e.stopPropagation?e.stopPropagation():e.cancelBubble=true; } } //方法2:通过对象事件获取关联元素 //方法可取但是没有解决性能问题,因为滑到子级上,还是会触发父级的鼠标事件,没有完成性能优化,此处要注意的是,显示隐藏代码一定要设置在判断元素之后 //在父级事件中代码执行前,判断关联元素的目的:是阻止父级事件中代码的执行,不会阻止冒泡事件的发生; oDiv.onmouseover=function (e) { console.log("in"); e= e||window.event; var oMo=e.fromElement || e.relatedTarget; if(this.contains(oMo)) return; console.log(2); oP.style.display="block"; }; oDiv.onmouseout=function (e) { console.log("out"); e= e||window.event; var oMo=e.toElement || e.relatedTarget; if(this.contains(oMo)) return; console.log(1); oP.style.display="none"; } //打印结果:从进入div,在出来进入p,然后再从p移出这个过程的打印结果为:in-2-out-in-out-1; //方法3:设置onmouseenter和onmouseleave事件,能够解决性能问题,事件只触发一次 oDiv.onmouseenter=function () { console.log("in"); oP.style.display="block"; }; oDiv.onmouseleave=function () { console.log("out"); oP.style.display="none"; } </script> </body> </html>
- 实例3:当鼠标移上div元素身上时,p显示;移出div元素后,p隐藏;强调:移出到任何位置,p都隐藏,移动p元素身上,也必须隐藏;
- 问题:当鼠标移动到子级p元素身上时,会触发p元素的鼠标事件,进而再次触发父级div的鼠标事件,这样就会重复触发,性能不好,同时当鼠标移动到子级p元素身上的时候,子级不会隐藏,所以要阻止子级的冒泡事件;
- 解决方法:1)在子级元素身上绑定同行为事件,然后在事件函数中阻止冒泡发生;2)在父级元素中,通过事件对象获取事件源,然后判断事件源的nodeName来获取元素TagName名,判断是否为父级元素标签,若不是,则阻止代码执行;
- 代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>需求2</title> <style> *{ margin: 0; padding: 0; } .div1{ width: 200px; height: 50px; background-color: lightseagreen; position: relative; margin: 20px auto; } .div1 p{ width: 500px; height: 300px; position: absolute; right: 0; top: 50px; background-color: lightpink; display: none; } </style> </head> <body> <div class="div1"> <p></p> </div> <script> var oDiv=document.getElementsByTagName("div")[0]; var oP=document.getElementsByTagName("p")[0]; //方法1:在子级事件函数中阻止冒泡发生; oDiv.onmouseover=function () { console.log("in"); oP.style.display="block"; oP.onmouseover=function (e) { e=e||window.event; //阻止子级的冒泡事件 e.stopPropagation?e.stopPropagation():e.cancelBubble=true; } }; oDiv.onmouseout=function () { console.log("out"); oP.style.display="none"; oP.onmouseout=function (e) { e=e||window.event; e.stopPropagation?e.stopPropagation():e.cancelBubble=true; } }; //方法2:在父级元素中判断事件源 oDiv.onmouseover=function(e){ e=e || window; e.target=e.target || e.srcElement; if(e.target.nodeName.toLowerCase!=="div") return; oP.style.display="block"; }; oDiv.onmouseout=function(e){ e=e || window; e.target=e.target || e.srcElement; if(e.target.nodeName.toLowerCase!=="div") return; oP.style.display="none"; }; </script> </body> </html>
- 实例1:当鼠标滑到div上时,新建一个p元素,让其跟随光标移动,当光标移出div后,p元素消失;
-
购物车点击版实例
- 需求:当点击div元素及p元素时,p元素显示,点击除二者以外的任何区域都让p隐藏
- 思路:如果一个父级容器下添加多个事件,那么就将事件委托给父级容器,通过判断事件源来进行相应的操作;
- 方法:1)通过开关思想,控制隐藏与显示,但是会存在一个问题,在第二个判断中如果不设置bOk=true,就会出现,第一次点击div后,p显示,然后点击外面,p消失,但此时的bOk为false,再次点击div时,会走else语句,不会显示;所以必须设置bOk=true; 2)通过判断p的行间样式是否为block来控制显示和隐藏;3)通过给父级容器添加点击事件,然后通过事件对象来获取事件源,判断事件源的nodeName是否为div和p的tagName名,若是二者之一,则设置p为显示,若都不是,则设置p为隐藏;
- 知识点:事件委托的应用环境及行间样式的获取
- 代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>购物车点击版实例</title> <style> *{ margin: 0; padding: 0; } html,body{ height: 100%; } .div1{ width: 200px; height: 50px; background-color: lightseagreen; position: relative; margin: 20px auto; } .div1 p{ width: 500px; height: 300px; position: absolute; right: 0; top: 50px; background-color: lightpink; display: none; } </style> </head> <body> <div class="div1"> <p></p> </div> <script> //需求:当点击div元素及p元素时,p元素显示,点击除二者以外的任何区域都让p隐藏 var oDiv=document.getElementsByTagName("div")[0]; var oP=document.getElementsByTagName("p")[0]; var bOk=true; //思路:如果一个父级容器下添加多个事件,那么就将事件委托给父级容器,通过判断事件源来进行相应的操作; //方法1:通过开关思想,控制隐藏与显示,但是会存在一个问题,在第二个判断中如果不设置bOk=true,就会出现,第一次点击div后,p显示,然后点击外面,p消失,但此时的bOk为false,再次点击div时,会走else语句,不会显示;所以必须天机bOk=true; document.body.onclick=function (e) { e=e||window; var target=e.target ||e.srcElement; if(target.className.toLowerCase()==="div1"){ if(bOk){ oP.style.display="block"; }else{ oP.style.display="none"; } bOk=!bOk; }else if(target.tagName.toLowerCase()!=="p"){ oP.style.display="none"; bOk=true; } }; //方法2:通过判断p的行间样式是否为block来控制显示和隐藏 //需注意:在第一次点击div时,此时oP.style.display拿到的值为空,因为拿到的是行间样式,行间没有设置,而在style中设置的都是非行间样式,通过style是不能获取的,所以第一次点击会执行else语句; document.body.onclick=function (e) { e=e||window; var target=e.target ||e.srcElement; if(target.className.toLowerCase()==="div1"){ if(oP.style.display==="block"){ oP.style.display="none"; }else{ oP.style.display="block"; } }else if(target.tagName.toLowerCase()!=="p"){ oP.style.display="none"; } } //方法3:通过判断事件源的nodeName来进行设置 document.body.onclick=function (e) { e=e || window.event; e.target=e.target || e.srcElement; if(e.target.nodeName==="DIV" || e.target.nodeName==="P"){ oP.style.display="block"; }else{ oP.style.display="none"; } } </script> </body> </html>
-
购物车点击版实例2
- 需求:当点击div时,p显示,当移出div后,p隐藏,但是保证从div移出到p元素身上时,p不隐藏,从p上移出后,p隐藏;
- 方法:1)用onclick和onmouseleave事件设置;2)用onclick和onmouseout事件设置,但是用onmouseout事件设置,会存在问题:当移出div到p元素身上后,p会隐藏;所以需要获取关联元素,阻断隐藏代码;
- 知识点:onmouseout与onmouseleave的区别,以及关联元素的获取及阻断代码执行;
- 代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>购物车实例</title> <style> *{ margin: 0; padding: 0; } .div1{ width: 200px; height: 50px; background-color: lightseagreen; position: relative; margin: 20px auto; } .div1 p{ width: 500px; height: 300px; position: absolute; right: 0; top: 50px; background-color: lightpink; display: none; } </style> </head> <body> <div class="div1"> <p></p> </div> <script> //需求:当点div时,p显示,当移出div后,p隐藏,但是保证从div移出到p元素身上时,p不隐藏,从p上移出后,p隐藏; //方法1:用点击事件和onmouseleave事件设置 var oDiv=document.getElementsByTagName("div")[0]; var oP=document.getElementsByTagName("p")[0]; oDiv.onclick=function () { oP.style.display="block"; }; //用onmouseleave设置,当移出div到p元素身上后,p不会隐藏; oDiv.onmouseleave=function () { oP.style.display="none"; }; //方法2:用onclick和onmouseout事件设置,但是用onmouseout事件设置,会存在问题:当移出div到p元素身上后,p会隐藏;所以需要获取关联元素,阻断隐藏代码; oDiv.onclick=function () { oP.style.display="block"; }; oDiv.onmouseout=function (e) { e=e||window.event; //获取关联元素,阻断隐藏代码执行; var omo=e.toElement || e.relatedTarget; if(this.contains(omo)) return; oP.style.display="none"; } </script> </body> </html>
知识点扩充
- 定位:
- 页面结构中元素A包含着B元素,A为父级元素,B为子级元素,B相对于A定位
- B的盒子模型是包含margin在内的大盒子,然后相对于A的边框内边缘定位,即若B的margin设置为0,并且定位中left也设置为0,则B的左边框外边缘与A的左边框内边缘贴合,当添加margin之后,二者之间会存在相应间隔;
- 总结:子级元素盒子模型(包含margin)相对于定位父级的边框内边缘进行定位;
网友评论