香草JavaScript事件代表团

MrG*_*uru 10 javascript

在vanilla js中进行事件授权的最佳方式(最快/最合适)是什么?

例如,如果我在jQuery中有这个:

$('#main').on('click', '.focused', function(){
    settingsPanel();
});
Run Code Online (Sandbox Code Playgroud)

我怎样才能将其翻译成香草js?或许有.addEventListener()

我能想到这样做的方式是:

document.getElementById('main').addEventListener('click', dothis);
function dothis(){
    // now in jQuery
    $(this).children().each(function(){
         if($(this).is('.focused') settingsPanel();
    }); 
 }
Run Code Online (Sandbox Code Playgroud)

但这似乎效率低下,特别是如果#main有很多孩子.

这是正确的方法吗?

document.getElementById('main').addEventListener('click', doThis);
function doThis(event){
    if($(event.target).is('.focused') || $(event.target).parents().is('.focused') settingsPanel();
}
Run Code Online (Sandbox Code Playgroud)

Ian*_*ark 11

我提出了一个简单的解决方案,似乎运行得相当好(尽管有传统的IE支持).在这里,我们扩展了EventTarget原型,以提供一种delegateEventListener使用以下语法工作的方法:

EventTarget.delegateEventListener(string event, string toFind, function fn)
Run Code Online (Sandbox Code Playgroud)

我已经创建了一个相当复杂的小提琴来演示它在行动中,我们委托绿色元素的所有事件.停止传播继续工作,您可以访问应该event.currentTarget通过的内容this(与jQuery一样).

这是完整的解决方案:

(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));
Run Code Online (Sandbox Code Playgroud)


Cer*_*nce 8

与其改变内置原型(这会导致代码脆弱并且经常会破坏事物),只需检查单击的元素是否具有.closest与您想要的选择器匹配的元素。如果是,请调用您要调用的函数。例如,要翻译

$('#main').on('click', '.focused', function(){
    settingsPanel();
});
Run Code Online (Sandbox Code Playgroud)

在 jQuery 之外,使用:

document.querySelector('#main').addEventListener('click', (e) => {
  if (e.target.closest('#main .focused')) {
    settingsPanel();
  }
});
Run Code Online (Sandbox Code Playgroud)

除非内部选择器也可能作为父元素存在(这可能很不寻常),否则将内部选择器单独传递给.closest(例如,.closest('.focused'))就足够了。

使用这种模式时,为了保持紧凑,我经常将代码的主要部分放在提前返回的下方,例如:

document.querySelector('#main').addEventListener('click', (e) => {
  if (!e.target.matches('.focused')) {
    return;
  }
  // code of settingsPanel here, if it isn't too long
});
Run Code Online (Sandbox Code Playgroud)

现场演示:

$('#main').on('click', '.focused', function(){
    settingsPanel();
});
Run Code Online (Sandbox Code Playgroud)
document.querySelector('#main').addEventListener('click', (e) => {
  if (e.target.closest('#main .focused')) {
    settingsPanel();
  }
});
Run Code Online (Sandbox Code Playgroud)