在Vanilla JS中进行事件委托(delegate)的最佳方式(最快/适当)是什么?

例如,如果我在jQuery中有此功能:

$('#main').on('click', '.focused', function(){
    settingsPanel();
});

如何将其翻译为Vanilla JS?也许用.addEventListener()
我可以想到的方式是:
document.getElementById('main').addEventListener('click', dothis);
function dothis(){
    // now in jQuery
    $(this).children().each(function(){
         if($(this).is('.focused') settingsPanel();
    });
 }

但这似乎效率很低,特别是如果#main有很多 child 的时候。

那么这是正确的方法吗?
document.getElementById('main').addEventListener('click', doThis);
function doThis(event){
    if($(event.target).is('.focused') || $(event.target).parents().is('.focused') settingsPanel();
}

最佳答案

I've come up with a simple solution 似乎工作得很好(尽管支持旧版IE)。在这里,我们扩展了EventTarget的原型(prototype),以提供一种delegateEventListener方法,该方法使用以下语法:

EventTarget.delegateEventListener(string event, string toFind, function fn)

我创建了一个相当复杂的 fiddle 来演示它的实际作用,在此我们将所有事件委托(delegate)给绿色元素。停止传播继续起作用,您可以通过event.currentTarget(与jQuery一样)访问this

这是完整的解决方案:
(function(document, EventTarget) {
  var elementProto = window.Element.prototype,
      matchesFn = elementProto.matches;

  /* Check various vendor-prefixed versions of Element.matches */
  if(!matchesFn) {
    ['webkit', 'ms', 'moz'].some(function(prefix) {
      var prefixedFn = prefix + 'MatchesSelector';
      if(elementProto.hasOwnProperty(prefixedFn)) {
        matchesFn = elementProto[prefixedFn];
        return true;
      }
    });
  }

  /* Traverse DOM from event target up to parent, searching for selector */
  function passedThrough(event, selector, stopAt) {
    var currentNode = event.target;

    while(true) {
      if(matchesFn.call(currentNode, selector)) {
        return currentNode;
      }
      else if(currentNode != stopAt && currentNode != document.body) {
        currentNode = currentNode.parentNode;
      }
      else {
        return false;
      }
    }
  }

  /* Extend the EventTarget prototype to add a delegateEventListener() event */
  EventTarget.prototype.delegateEventListener = function(eName, toFind, fn) {
    this.addEventListener(eName, function(event) {
      var found = passedThrough(event, toFind, event.currentTarget);

      if(found) {
        // Execute the callback with the context set to the found element
        // jQuery goes way further, it even has it's own event object
        fn.call(found, event);
      }
    });
  };

}(window.document, window.EventTarget || window.Element));

09-19 01:35