闭包事件委托 - 在DOM父元素上添加事件监听器,覆盖给定类的子元素/后代元素

7
在 jQuery 中,您可以执行以下操作:
$('#j_unoffered').on('click', '.icon_del', function () {...

这将在元素j_unoffered上放置一个处理程序,如果任何带有类icon_del的后代元素被点击,则触发该处理程序。 它还适用于随后创建的任何icon_del元素。

我可以在Closure中使其正常工作,其中单击在元素本身上。

goog.events.listen(
    goog.dom.getElement('j_unoffered'),  
    goog.events.EventType.CLICK,
    function(e) {...

如何在Closure中指定一个父事件目标,使其对其子/后代起作用,就像jQuery示例一样?
我假设我需要以某种方式使用setParentEventTarget,但我不确定如何为DOM事件实现它。我找到的大部分文档都涉及自定义派发事件。
-- 更新 --
我想知道这个非常简单的解决方案是否存在问题:
goog.events.listen(
    goog.dom.getElement('j_unoffered'),  
    goog.events.EventType.CLICK,
    function(e) {
        if (e.target.className.indexOf('icon_del') !== -1) {...

这仍然将this绑定到父级,但e.target提供了一种解决方法。在listen中的第五个参数(opt_handler)允许您将this绑定到其他内容,所以我想那也是一种选择。

2个回答

5

我对这种可能性也不太清楚,所以我建议使用其他代码:

var addHandler = function(containerSelector, eventType, nestSelector, handler) {
    var parent = goog.isString(containerSelector) ? 
                 document.querySelector(containerSelector) :
                 containerSelector;

    return goog.events.listen(
        parent,
        eventType,
        function(e) {

            var children = parent.querySelectorAll(nestSelector);
            var needChild = goog.array.find(children, function(child) {
                return goog.dom.contains(child, e.target);
            });

            if (needChild)
                handler.call(needChild, e);                

        });
});

使用方法:

addHandler('#elem', goog.events.EventType.CLICK, '.sub-class', function(e) {
    console.log(e.target);
});

更新:

如果你使用 e.target.className.indexOf('icon_del'),有可能会错过正确的事件。考虑一个id为container的容器div,其中包含几个class为innerContainer的div,每个内部还包含几个class为finalDiv的div。假设你使用上面的代码添加事件处理程序,并检查e.target是否具有innerContainer类,则在用户单击finalDiv时,将会调用你的处理程序,但事件目标将是finalDiv,而不是innerContainer,因为它被包含在其中。你的代码将错过这个,但它不应该。我的代码检查e.target是否具有嵌套类或被包含在其中,因此你将不会错过这样的事件。

opt_handler也无法真正帮助你,因为你可能想要处理许多嵌套元素(你将在这里传递哪一个?也许所有,但并不那么有用,你可以在事件处理程序中随时获取它们),而且它们可能是动态添加的,所以当你添加处理程序时,你可能不知道它们的存在。

总之,在事件处理程序中执行此类任务是合理和高效的。


+1 对于一个好的开始来说不错,但我相信 Google 的员工之前一定遇到过这个问题,所以我不想重复造轮子。请注意我的更新。 - Nick
是的,它可以工作,但是我理解你需要更通用的方法。我从演示中删除了 on 调用,并使用了 addEventListener。它的效果相同:http://jsfiddle.net/g3Vj6/2/。我认为你需要在这里使用 DOM 遍历,就像我的代码中一样。 - Tony
它无法识别 span 的原因是因为 e.targeta!! 尝试使用 console.log(e.target); :) - Nick
我知道!这正是我想要展示的。你问“这个简单的解决方案有什么问题”,我展示了在某些情况下它不起作用,代码需要更复杂,这不是你想要的吗? - Tony
我也使用Google Closure,我也感到惊讶,但似乎没有内置的方法来实现这一点。你不是第一个提出这个问题的人:https://dev59.com/vGbWa4cB1Zd3GeqPUTIm https://groups.google.com/forum/#!topic/closure-library-discuss/QAIugT6rZOA 但是并没有答案。Closure使用其他范例,而且在任何情况下都可以找到如何使用直接事件处理而不是事件委托(将它们附加在enterDocument中)。正如我之前所说,Closure使用其他范例。 - Tony
显示剩余2条评论

3
你提到的是event delegation
看起来使用Google Closure Library无法直接实现这个功能,因此我的建议是使用jQuery或其他类似的事件处理库来实现。如果这不可行,或者你想手动实现,这里有一个可能的方法(注意:这不适用于生产环境)。
var delegateClick = function(containerId, childrenClass, handler){
  goog.events.listen(goog.dom.getElement(containerId), goog.events.EventType.CLICK, function(event){
    var target = event.target;
    //console.log(event);
    while(target){
      if ( target.className && target.className.split(" ").indexOf(childrenClass)!== -1) {
        break;
      }
      target = target.parentNode;
    }
    if(target){
      //handle event if still have target
      handler.call(target, event);
    }
  });
}
//then use it, try this here: http://closure-library.googlecode.com/git/closure/goog/demos/index.html
//..select the nav context
delegateClick( 'demo-list' ,'goog-tree-icon', function(event){console.log(event);})

这是event delegation的更深入分析:

同样,您应该使用经过验证的库来处理此任务,以下是一些选项:jQueryZeptoBean


感谢“事件委托”这个术语。我使用它,只是记不得它叫什么:) 我认为暗示Closure不是一个“经过验证的库”有点牵强。但是对于一个好的开始,我给你加一分。请看我的更新。 - Nick
@Nick,我建议使用经过验证的库(任何库)来处理/委托事件,而不是自己编写。我并不是在暗示Closure不是一个好的库。祝好 :) - MarianCJC
你好。为什么你说“这不适用于生产环境”? - Nick

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接