web-dev-qa-db-ja.com

バニラJSイベント委任-ターゲット要素の子要素の処理

VanillaJSでイベントの委任をしようとしています。このようなコンテナの中にボタンがあります

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

そして次の David Walshの素敵な指示 私は次のようにボタンの祖先にイベントハンドラーを追加しています:

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

どこ this.containerは#quiz要素です。これは半分の時間で機能しますが、残りの時間はクリックイベントのターゲットがボタン内のスパンの1つであるため、イベントハンドラーは呼び出されません。この状況に対処するための最良の方法は何ですか?

21
And Finally

代替ソリューション:

MDN:ポインタイベント

ネストされたすべての子要素にクラスを追加します(.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に設定すると、クリックイベントはこれらの要素で発生しません。

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

4
C_Ogoo

新しいブラウザ

新しいブラウザは .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');
    }
});
38