DOM事件流:“DOM二级事件”规定的事件流包含三个阶段:事件捕获,处于目标,事件冒泡。
事件捕获:不太具体的节点先捕获接收到事件,然后传递到具体的节点。
事件冒泡:事件开始时由最具体的元素接收,然后逐级上上传播到最不具体的节点。
那么,事件冒泡有什么作用呢?
首先,我们页面里有很多DOM事件有处理程序。比如说:
<body> <div class="box" style="width:100px;height:"100px";" oncick="fn1"></div> <script> function fn1(){ alert("点击事件") } </script> </body>
那么,如果我有很多DOM元素需要添加事件处理程序呢?比如说,我有100个li元素需要添加事件处理程序呢?难道一个一个添加吗?
<style type="text/css">
#outSide{
width: 540px;
padding: 20px;
background: lightblue;
margin: 0 auto;
}
#outSide li{
width: 500px;
height: 50px;
background: lightcoral;
border: 1px solid lightgoldenrodyellow;
}
</style>
<body> <ul> <li style="width:100px;height:"100px";" oncick="fn1()"></li> <li style="width:100px;height:"100px";" oncick="fn1()"></li> <li style="width:100px;height:"100px";" oncick="fn1()"></li> <li style="width:100px;height:"100px";" oncick="fn1()"></li> <script> function fn1(){ alert("点击事件") } </script> </body>
这样显然不是一个明智的方法,当然,还有一种方法:架for循环
<body> <ul id="outSide"> <li id="name">1</li> <li id="age">2</li> <li id="sex">3</li> <li id="school">4</li> <li id="home">5</li> </ul> <script> var oUl = document.getElementById("outSide");var aLi = oUl.getElementsByTagName("li"); for(var i = 0; i < aLi.length;i++) { a[i].onclick = function(){ console.log("click事件") } } </script> </body>
用for循环来做的话,少数的当然是可以的,但是如果有10000个甚至更多的li呢?
问题在于:添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能。
1、每访问一次DOM都会引起浏览器的重绘和重排。多次访问DOM就会延长整个页面的交互就绪时间。所以尽可能减少访问DOM次数可以性能优化。
2、每个函数都是对象,都会占用内存;内存中的对象越多,性能越差。
3、代码运行时,首先要找到ul,然后遍历li。之后点击li的时候,又要再一次找到目标li的位置,最后才能执行操作。所以每次点击都要找一次li,这样浪费时间内存。
所以,我们使用事件委托来处理这件事就容易得多了。使用事件委托时,我们把事件处理程序添加到其父元素(在例子中就是ul)上,然后按照事件冒泡的规则,点击到子元素(li)上的时间就会一级一级冒到父元素上。如下:
<script> var oUl = document.getElementById("outSide"); oUl.onclick = function(){ console.log("click事件"); } </script>
有人会说,如果我只想让点击到li的时候事件处理程序执行,点击ul不包含li的时候(如ul的padding)不执行,怎么办呢?
其实每个事件都会有一个事件源,就是你真正操作的部分。Event对象提供了的target属性就是事件源,用于捕获真正被点击的节点元素。
获取target属性: IE下:window.event.srcElement
标准浏览器下:event.target
这样的话,我们就可以通过给父元素添加监听事件,在事件触发监听器的时候,通过事件源的判断事件源的nodeName(事件源元素的标签名)来判断是否是li元素;
那么,又为什么我们要用事件监听呢?什么是事件监听呢?
事件监听就是让绑定事件监听的元素等待监听规定的事件的发生,发生后作出相应的反应。听起来好像跟直接绑定具体事件没有什么区别,那为什么要使用事件监听呢?其实是这样的:
1、使用事件监听可以为一个元素绑定多个事件。常规对同一个元素绑定多个相同事件的时候(如多个onclick事件),后面的事件处理会覆盖前面的事件处理,不管你绑定多少个事件处理,只执行最后一个。而使用事件监听就可以每个都执行。
2、使用事件监听当不需要的时候还可以解除相应的事件绑定,非常方便。
附兼容各大主浏览器的监听事件和移除监听事件原生:(true:捕获;false:冒泡;默认false)
function addEventHandler(target,type,func){ if(target.addEventListener){//监听IE9,谷歌和火狐 target.addEventListener(target,func,false); }else if(target.attachEvent){ target.attachEvent("on"+type,func); }else{ target["on" + type] = func; } }
function removeEventHandler(target,type,func){ if(target.removeEventListener){ target.removeEventListener(type,func,false); }else if(target.detachEvent){ target.detachEvent("on" + type,func); }else{ delete target["on"+type]; } }
具体实现如下:
oUl.addEventListener('click',function(ev){ var ev = event||window.event; var target = ev.target || ev.srcElement; if(target.nodeName.toLowerCase()=='li'){ alert('li'); } },false)
除此之外呢,我们还可以利用冒泡原理让不同的对象同时捕获同一事件,并且执行不同的事件处理程序:
<body> <div onclick="outSideWork()" id="outSide" style="width: 140px;height: 100px; background: lightblue; padding: 20px;" > <div onclick="inSideWork()" id="inSide" style="width:100px; height:100px; background:#CCC"></div> </div> <script> function outSideWork() { console.log('父元素获取'); } function inSideWork() { console.log('子元素获取'); } </script> </body>
Event对象提供了的target属性就是事件源,用于捕获真正被点击的节点元素。
target.nodeName:事件源的标签名(大写)
还有一种情况,就是我们需要给点击不同元素时绑定不同的操作,这个时候又该怎么办呢?很简单,只要我们给每个li元素设定不同的id,通过判断id就可以为不同的li元素绑定不同的处理程序了。
<ul id="outSide" > <li id="name">1</li> <li id="age">2</li> <li id="sex">3</li> <li id="school">4</li> <li id="home">5</li> </ul> </body> <script> var oUl = document.getElementById("outSide"); var a=document.getElementById('a'); var b=document.getElementById('b'); var c=document.getElementById('c'); oUl.addEventListener("click",function(event){ var event=event||window.event; var target=event.target||event.srcElement; if(target.nodeName.toLowerCase()=='li'){ switch (target.id){ case "name":console.log("name");break; case "age":console.log("age");break; case "sex":console.log("sx");break; case "school":console.log("school");break; case "home":console.log("home");break; } } },false);
上面都是DOM节点中已经存在的元素,那么如果动态添加li元素,并且让每个li元素点击之后打印出他们所在的下标,怎么办呢?
<body> <ul id="outSide" > <li id="name">1</li> <li id="age">2</li> <li id="sex">3</li> <li id="school">4</li> <li id="home">5</li> </ul> <input type="button" id="add" value="添加" /> <script> var oUl = document.getElementById("outSide"); var oBtn = document.getElementById("add"); var aLi = oUl.getElementsByTagName("li"); oUl.addEventListener("click",function(event){ var event = event || window.event; var target = event.target || event.srcElement; for(var i = 0; i < aLi.length;i++) { if(aLi[i] == target) { console.log(i+1); } } },false); oBtn.addEventListener("click",function(){ var new_li = document.createElement("li"); new_li.innerHTML = "new li"; oUl.appendChild(new_li); },false); </script> </body>
来源:https://www.cnblogs.com/cencenyue/p/7572044.html