×

event delegation -- 事件委托

96
laohan
2016.07.19 23:00* 字数 442

事件代理或者说是事件委托,就是利用事件的冒泡特性,将事件绑定到外层元素上,而不是触发事件的元素上。可以减少绑定的事件数量,而且对于动态加载的内容来说,十分有利。

// html
<ul id="lists">
    <li class="list"></li>
    <li class="list"></li>
    <li class="list"></li>
</ul>

// js
var lists = document.getElementById('lists');
var delegate = function(element, selector, event, hanlder) {
    element.addEventListener(event, function(e) {
        var target = e.target;
        if(!target.matches(selector)) {
            return;
        }
        hanlder(e);
    });
};

delegate(lists, 'li', 'click', function(e){console.log('event delegation')});

上面这个代码,应该是我们经常看到的简易版的事件委托。把点击事件绑定到ul上,只有点击的元素为li时,这个事件处理函数才被执行。

这样做的好处就是

  1. 如果事件是绑定在li上时,则需要绑定三次事件,而现在事件绑定在ul上,绑定次数减少到一次
  2. 如果事件是绑定是li上时,后面还要动态加载li元素时,还要继续为加载的li元素绑定事件,而现在事件绑定在ul上,就无需绑定了。

这样写,也明显有个不足,就是如果触发事件的元素不是li,而是li的子元素时,上面的那个事件不会被触发。但讲道理,li子元素触发点击事件,也是可以认为是li触发点击事件的。

所以,我们可以按照下面的方式实现事件委托。

// html
<ul id="lists">
    <li class="list"><span>list 1</span></li>
    <li class="list"><span>list 2</span></li>
    <li class="list"><span>list 3</span></li>
</ul>

// js
var lists = document.getElementById('.lists');
var isParentOfTarget = function(selector, target, until) {
    if(!until) {
        until = document.documentElement;
    }
    var parent = target;
    while(parent != until) {
        if(parent.matches(selector)) {
            return parent;
        }
        parent = parent.parentNode;
    }
    return false;
}

var delegate = function(element, selector, event, hanlder) {
    element.addEventListener(event, function(e) {
      var target = e.target;
      var res = isParentOfTarget(selector, target, element)
      if(!res) {
        return;
      }
      hanlder.call(res, e);
    }, false);
}
delegate(lists, 'span', 'click', function(e){console.log('evnet delegation');});

通过isParentOfTarget函数,判断触发事件的元素(即event.target),是不是我们需要委托的元素的子元素,如果是,则返回这个委托的元素,不是,就返回false。

最后,再利用call函数,将事件处理函数的this绑定到所需委托的元素上。

js
Web note ad 1