Vanilla JS event delegation - dealing with child elements of the target element

喜夏-厌秋 提交于 2019-11-26 18:18:21

问题


I'm trying to do event delegation in vanilla JS. I have a button inside a container like this

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

And following David Walsh's nice instructions I'm adding an event handler to an ancestor of the button like so:

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

Where this.container is the #quiz element. This works half the time, but the rest of the time the target of the click event is one of the spans inside the button, so my event handler isn't called. What's the best way to deal with this situation?


回答1:


Alternate Solution:

Add a class to all nested child elements (.pointer-none)

/* css */
.pointer-none {
  pointer-events: none;
}

Your mark-up becomes

<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>

With the pointer set to none, the click event wouldn't fire on those elements.

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




回答2:


Newer browsers

Newer browsers support .matches:

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

You can get the unprefixed version with

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

And then use .apply for more browsers (Still IE9+).

Older browsers

Assuming you have to support older browsers, you can walk up the 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
}

However, this will climb the whole dom, and you want to stop at the delegation target:

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
}

Which, would make your code:

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');
    }
});


来源:https://stackoverflow.com/questions/24117369/vanilla-js-event-delegation-dealing-with-child-elements-of-the-target-element

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!