在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));