【问题标题】:Vanilla JS event delegation - dealing with child elements of the target elementVanilla JS 事件委托 - 处理目标元素的子元素
【发布时间】:2014-06-09 09:31:33
【问题描述】:

我正在尝试在 vanilla JS 中进行事件委托。我在这样的容器中有一个按钮

<div id="quiz">
    <button id="game-again" class="game-again">
        <span class="icon-spinner icon"></span>
        <span>Go again</span>
    </button>
</div>

David Walsh's nice instructions 之后,我正在向按钮的祖先添加一个事件处理程序,如下所示:

this.container.addEventListener('click', function(e){
    if (e.target && e.target.id == 'game-again') {
        e.stopPropagation();
        self.publish('primo:evento');
    }
});

其中this.container 是#quiz 元素。这在一半时间有效,但其余时间单击事件的目标是按钮内的跨度之一,因此不调用我的事件处理程序。处理这种情况的最佳方法是什么?

【问题讨论】:

  • 你需要支持哪些浏览器?
  • IE9+ 和现代主流
  • 那么你可以使用.matches,只要你得到无前缀的版本并使用matches.call(e.target,"#game-again,#game-again *")——更多细节请看我的回答。
  • 如果您想要更通用的事件委托实现,以及一些仍然是原生 JavaScript 的便捷增强功能,请查看 Oxydizr。它使用 HTML5 数据属性,因此您可以将行为与样式完全分离:&lt;button data-action="tasks.remove"&gt;X&lt;/button&gt;。您可以将自定义数据传递给从data-action-params HTML5 数据属性获取的每个操作处理程序。
  • 谢谢 Greg,我去看看。

标签: javascript dom publish-subscribe event-delegation


【解决方案1】:

较新的浏览器

较新的浏览器支持.matches:

this.container.addEventListener('click', function(e){
    if (e.target.matches('#game-again,#game-again *')) {
        e.stopPropagation();
        self.publish('primo:evento');
    }
});

你可以得到不带前缀的版本

var matches = document.body.matchesSelector || document.body.webkitMatchesSelector || document.body.mozMatchesSelector || document.body.msMatchesSelector || document.body.webkitMatchesSelector

然后在更多浏览器上使用.apply(仍然是IE9+)。

旧版浏览器

假设你必须支持旧的浏览器,你可以走上 DOM:

function hasInParents(el,id){
    if(el.id === id) return true; // the element
    if(el.parentNode) return hasInParents(el.parentNode,id); // a parent
    return false; // not the element nor its parents
}

但是,这会爬上整个 dom,而你想在委托目标处停下来:

function hasInParentsUntil(el,id,limit){
    if(el.id === id) return true; // the element
    if(el === limit) return false;
    if(element.parentNode) return hasInParents(el.parentNode,id); // a parent
    return false; // not the element nor its parents
}

这将使您的代码:

this.container.addEventListener('click', function(e){
    if (hasInParentsUntil(e.target,'game-again',container)) { // container should be 
        e.stopPropagation();                                  // available for this
        self.publish('primo:evento');
    }
});

【讨论】:

  • 漂亮!谢谢本杰明。
  • 不客气,如果您对代码及其工作方式有任何疑问,请告诉我它是如何工作的。
  • 谢谢,所以我得到了你建议的无前缀版本,并设置了我的条件“if (matches.apply(e.target, ['#game-again,#game-again *']) )" 并且工作正常。
  • 您在示例中输入了两次document.body.matchesSelector
  • @Tomas 答案是为了回答问题。预计读者能够使用自己的批判性思维技能来确定答案的哪些部分与自己的需求相关。如果您认为可以写出更好的答案,我鼓励您这样做。
【解决方案2】:

替代解决方案:

MDN: Pointer events

为所有嵌套的子元素添加一个类 (.pointer-none)

.pointer-none {
  pointer-events: none;
}

你的标记变成了

<div id="quiz">
    <button id="game-again" class="game-again">
        <span class="icon-spinner icon pointer-none"></span>
        <span class="pointer-none">Go again</span>
    </button>
</div>

将指针设置为 none 时,不会在这些元素上触发 click 事件。

https://css-tricks.com/slightly-careful-sub-elements-clickable-things/

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-04-14
    • 1970-01-01
    • 2020-05-16
    • 2012-08-15
    • 1970-01-01
    • 1970-01-01
    • 2021-11-02
    • 1970-01-01
    相关资源
    最近更新 更多