2014-05-07 107 views
8

在香草js中做事件代理的最佳方式(最快/最合适)时尚是什么?香草JavaScript事件代表

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

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

如何翻译,为香草JS?也许.addEventListener()

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

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

但似乎效率不高,特别是如果#main有许多儿童。

这是做到这一点的正确方法吗?

document.getElementById('main').addEventListener('click', doThis); 
function doThis(event){ 
    if($(event.target).is('.focused') || $(event.target).parents().is('.focused') settingsPanel(); 
} 
+4

从'event.target'开始,查看它是否与选择器匹配,如果是,则调用该函数。然后遍历它的祖先,并且一直到这个'this'元素。你可以使用'node.matchesSelector()'来做测试,尽管在某些浏览器中它是用一个特定于浏览器的标志来实现的,所以你需要将它包装在一个函数中,或者把标记方法放在Element上。 prototype.matchesSelector' –

+1

建议你发布这个答案。 –

+0

@EvanTrimboli:我不喜欢发布答案。我会让其他人得到代表。如果你愿意,请随意。 –

回答

9

I've come up with a simple solution这似乎运作得相当好(旧版IE的支持,尽管)。在这里,我们延长EventTarget的原型提供了delegateEventListener方法使用以下语法其中工程:

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

我创建了一个相当复杂的fiddle证明它在行动,我们委派的所有事件绿色元素。停止传播继续工作,你可以访问应该是event.currentTargetthis(与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)); 
+0

Thx,伟大的解决方案! – jaydoubleyou

+0

你应该首先检查标准的前缀版本。 – gilly3

+0

@ gilly3我也应该计算一次,而不是每次。我会更新 –

0

我有一个类似的解决方案来实现事件委托。 它利用阵列功能slice,reverse,filterforEach

  • slice将NodeList从查询转换为数组,必须在允许颠倒列表之前完成该数组。
  • reverse反转阵列(使得最终traversion开始尽量靠近事件目标成为可能。
  • filter检查哪些元素包含event.target
  • forEach要求从只要经过滤的结果中的每个元件所提供的处理程序作为处理不返回false

该函数返回创建的委托功能,这使得它可以以后删除监听器。 注意,本地event.stopPropagation()确实ñ不要停止穿越validElements,因为冒泡阶段已经遍历到委托单元。

function delegateEventListener(element, eventType, childSelector, handler) { 
    function delegate(event){ 
     var bubble; 
     var validElements=[].slice.call(this.querySelectorAll(childSelector)).reverse().filter(function(matchedElement){ 
      return matchedElement.contains(event.target); 
     }); 
     validElements.forEach(function(validElement){ 
      if(bubble===undefined||bubble!==false)bubble=handler.call(validElement,event); 
     }); 
    } 
    element.addEventListener(eventType,delegate); 
    return delegate; 
} 

尽管不推荐延长天然原型,该功能可以(以IE或Node)被添加到原型EventTarget。这样做时,请在功能中将element替换为this,并删除相应的参数(EventTarget.prototype.delegateEventListener = function(eventType, childSelector, handler){...})。