js的事件委托

一道前端面试题开始:现有一个包含三个li的无序列表ul,点击每一个li时alert里边的内容,如何实现?

<ul id="list">
     <li>1</li>
     <li>2</li>
     <li>3</li>
</ul>

首先想到的是:

  var ndItem = document.getElementsByTagName('li');
for(var i=0;i<3;i++){
    ndItem[i].addEventListener('click',   function () {
        alert(i);
    });
}

那么如果li的数量变了,不是3个,变成300个了呢?如果继续通过上述方法,那么dom注册的监听事件就会变为原来的100倍,显然这样就不合适了,那么,事件委托 正好可以解决这个问题。

事件委托,又叫事件代理,Js高级 中这样描述:事件委托利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。

(DOM事件流包括三个阶段:事件捕获阶段->处于目标阶段->事件冒泡阶段。

    IE的事件流叫做事件冒泡,即事件开始时由最具体的元素接收,然后逐渐向上传播到较为不具体的节点。

    事件捕获:由不太具体的节点应该更早接收到事件,而具体的节点最后接收到事件。

)

用事件委托实现上边案例:

document.getElementById("list").addEventListener('click', function(e){
    alert('点击内容是:' + e.target.innerText);
})

这里用父级ul做事件处理,当li被点击时,由于冒泡原理,事件就会冒泡到ul上,因为ul上有点击事件,所以事件就会触发,当然,这里当点击ul的时候,也是会触发的,那么问题就来了,如果我想让事件代理的效果跟直接给节点的事件效果一样怎么办,比如说只有点击li才会触发,接着往下看:


Event对象提供了一个属性叫target,可以返回事件的目标节点,我们成为事件源,也就是说,target就可以表示为当前的事件操作的dom,但是不是真正操作dom,当然,这个是有兼容性的,标准浏览器用ev.target,IE浏览器用event.srcElement,此时只是获取了当前节点的位置,并不知道是什么节点名称,这里我们用nodeName来获取具体是什么标签名,这个返回的是一个大写的,我们需要转成小写再做比较(习惯问题):

window.onload = function(){
  var oUl = document.getElementById("list");
  oUl.onclick = function(ev){
    var ev = ev || window.event;
    var target = ev.target || ev.srcElement;
    if(target.nodeName.toLowerCase() == 'li'){
         alert(target.innerHTML);
    }
  }
}

如上就只有点击li会触发事件了,且每次只执行一次dom操作,如果li数量很多的话,将大大减少dom的操作,优化的性能可想而知!

再比如说,我现在换了个题目:

<div id="box">
        <input type="button" id="add" value="添加" />
        <input type="button" id="remove" value="删除" />
        <input type="button" id="move" value="移动" />
        <input type="button" id="select" value="选择" />
</div>

也可以使用事件委托的方式:

window.onload = function(){
            var oBox = document.getElementById("box");
            oBox.onclick = function (ev) {
                var ev = ev || window.event;
                var target = ev.target || ev.srcElement;
                if(target.nodeName.toLocaleLowerCase() == 'input'){
                    switch(target.id){
                        case 'add' :
                            alert('添加');
                            break;
                        case 'remove' :
                            alert('删除');
                            break;
                        case 'move' :
                            alert('移动');
                            break;
                        case 'select' :
                            alert('选择');
                            break;
                    }
                }
            }
            
        }

再再比如说,有下面这样的场景,可以手动添加新的li

<input type="button" name="" id="btn" value="添加" />
    <ul id="ul1">
        <li>111</li>
        <li>222</li>
        <li>333</li>
        <li>444</li>
    </ul>

初始方法:

window.onload = function(){
            var oBtn = document.getElementById("btn");
            var oUl = document.getElementById("ul1");
            var aLi = oUl.getElementsByTagName('li');
            var num = 4;
            
            //鼠标移入变红,移出变白
            for(var i=0; i<aLi.length;i++){
                aLi[i].onmouseover = function(){
                    this.style.background = 'red';
                };
                aLi[i].onmouseout = function(){
                    this.style.background = '#fff';
                }
            }
            //添加新节点
            oBtn.onclick = function(){
                num++;
                var oLi = document.createElement('li');
                oLi.innerHTML = 111*num;
                oUl.appendChild(oLi);
            };
        }

如上虽然添加了新的标签,但是可以发现,事件没有添加上,那么可以通过事件委托来优化:

window.onload = function(){
            var oBtn = document.getElementById("btn");
            var oUl = document.getElementById("ul1");
            var aLi = oUl.getElementsByTagName('li');
            var num = 4;
            
            //事件委托,添加的子元素也有事件
            oUl.onmouseover = function(ev){
                var ev = ev || window.event;
                var target = ev.target || ev.srcElement;
                if(target.nodeName.toLowerCase() == 'li'){
                    target.style.background = "red";
                }
                
            };
            oUl.onmouseout = function(ev){
                var ev = ev || window.event;
                var target = ev.target || ev.srcElement;
                if(target.nodeName.toLowerCase() == 'li'){
                    target.style.background = "#fff";
                }
                
            };
            
            //添加新节点
            oBtn.onclick = function(){
                num++;
                var oLi = document.createElement('li');
                oLi.innerHTML = 111*num;
                oUl.appendChild(oLi);
            };
        }

最后,总结一下适用事件委托的事件包括:click、mousedown、mouseup、keydown、keyup和keypress。

值得注意的是:mouseover和mouseout事件也冒泡,但是要处理起来不太容易,而且经常需要计算元素的位置。








猜你喜欢

转载自blog.csdn.net/redatao/article/details/80604134