实时绑定事件处理程序

为现在、将来和给定选择器匹配的所有元素附加事件处理程序


为了在匹配的选择器的任何将来元素上自动绑定事件处理程序,我们使用了一个小技巧: 实际上,将事件处理程序绑定到文档而不是绑定到元素的事件。 事件在树上冒出气泡,当触发文档的事件处理程序时,我们检查事件的来源,看它是否与实时绑定选择器匹配:


// helper for enabling IE 8 event bindings
function addEvent(el, type, handler) {
    if (el.attachEvent) el.attachEvent('on'+type, handler); else el.addEventListener(type, handler);
}

// live binding helper
function live(selector, event, callback, context) {
    addEvent(context || document, event, function(e) {
        var found, el = e.target || e.srcElement;
        while (el && !(found = el.id == selector)) el = el.parentElement;
        if (found) callback.call(el, e);
    });
}

// example use
live('foo', 'click', function(){ console.log('Clicked #foo'); });
      

注意: 实际上,这不能完全替代jQuery的$.live()方法:在本示例中live()函数专门检查被单击元素的ID是否匹配“foo”。 测试其他选择器需要修改while语句el.id == elementId。 您可以选择检查类名称或任何其他属性/值。

当需要任意匹配的选择器时,请使用以下实时绑定代码。 虽然有点慢,但是可以达到和jQuery的$.live()方法一样的灵活性:


function live(selector, event, callback, context) {
  addEvent(context || document, event, function(e) {
      var qs = (context || document).querySelectorAll(selector);
      if (qs) {
          var el = e.target || e.srcElement, index = -1;
          while (el && ((index = Array.prototype.indexOf.call(qs, el)) === -1)) el = el.parentElement;
          if (index > -1) callback.call(el, e);
      }
  });
}
      

最后,这是一个性能优化的版本,该版本将现代DOM4方法与针对旧版浏览器的polyfill结合使用:


// matches polyfill
this.Element && function(ElementPrototype) {
    ElementPrototype.matches = ElementPrototype.matches ||
    ElementPrototype.matchesSelector ||
    ElementPrototype.webkitMatchesSelector ||
    ElementPrototype.msMatchesSelector ||
    function(selector) {
        var node = this, nodes = (node.parentNode || node.document).querySelectorAll(selector), i = -1;
        while (nodes[++i] && nodes[i] != node);
        return !!nodes[i];
    }
}(Element.prototype);

// live binding helper using matchesSelector
function live(selector, event, callback, context) {
    addEvent(context || document, event, function(e) {
        var found, el = e.target || e.srcElement;
        while (el && el.matches && el !== context && !(found = el.matches(selector))) el = el.parentElement;
        if (found) callback.call(el, e);
    });
}
      

通过使用适当的closet()方法而不是while语句,可以进一步优化此版本。