在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)
与其改变内置原型(这会导致代码脆弱并且经常会破坏事物),只需检查单击的元素是否具有.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)