JS的事件委托

Flchy 于 2017-11-21 发布

在用jQuery的时候就知道对于表格的行或者列表的项添加事件的时候要使用on而不是直接添加事件。因为列表项和表格的内容经常是或被更新的,如果对一个位置添加事件,当这个位置的html被替换后,之前添加的时间也会消失的

$(selector).on(event,childSelector,data,function)

后来才知道这个叫做事件委托-_-

最近再写播放器的时候也遇到了使用的场景,就是播放器的播放列表的点击事件,由于是使用原生JS写的,所以查阅了下关于JS时间委托的内容

2-1

JS时间委托的原理很简单,就是利用了事件冒泡,对于table、ul、ol来说,里面的tr和li是经常变化的,而最外层的table、ul、ol是不变的,所以可以设置事件在table、ul、ol身上,当事件传播到上层的时候可以判断子元素有没有被委托的事件,有就执行,就是这么简单。。。

错误代码,为好多元素添加了事件,而且不能适li的动态变化

var lis = document.getElementById('ul').getElementsByTagName('li');
for (var i = 0; i < lis.length; i++) {
    lis[i].onclick = function () {
        alert('ok');
    }
}

事件触发的DOM的Event对象提供了一个属性叫target,代表事件触发位置的DOM

所以可以这么写

var ul = document.getElementById('ul');
ul.addEventListener("click", function (ev) {
    var ev = ev || window.event;
    var target = ev.target || ev.srcElement;
    if (target.nodeName.toLowerCase() === 'li') {
        alert('ok');
    }
});

但是对于下面的情况,当点击a或者p标签的时候,上面的方法并不执行,因为target.nodeName为a或p并不是li

<ul id="ul">
    <li>
        <a>45</a>
        <p>12</p>
    </li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
</ul>

所以改造代码如下

var ul = document.getElementById('ul');
ul.addEventListener("click", function (ev) {
    var ev = ev || window.event;
    var target = ev.target || ev.srcElement;
    while (target !== ul) {
        if (target.tagName.toLowerCase() === 'li') {
            alert('ok');
            break;
        }
        target = target.parentNode;
    }
});

或者下面,path是冒泡的路径

var ul = document.getElementById('ul');
ul.addEventListener("click", function (ev) {
    var ev = ev || window.event;
    var path = ev.path;
    for (var i = 0; i < path.length; i++) {
        if (path[i] == ul) {
            break;
        }
        if (path[i].nodeName.toLowerCase() === 'li') {
            alert('ok');
        }
    }
});

参考

js中的事件委托或是事件代理详解