Closed11

Event Delegationをちょっと調べて、pointer-events: noneを置き換える

itsumoonazicodeitsumoonazicode

この前公開した記事で、「動的生成の要素のそれぞれにイベントハンドラを設定するには、その要素それぞれを囲むコンテナーにイベントを設定すると良いんだよ」って書いた。

itsumoonazicodeitsumoonazicode

こう書いてたのを…

改善前.js
const taskCompletedButtons = document.querySelectorAll('.btn-completed');
taskCompletedButtons.forEach((e) => {
    e.addEventListener('click', (j) => {
        if(j.target.className === 'Hello') {
            // なにか処理がはいります
        }
    });
});
itsumoonazicodeitsumoonazicode

こう書くようにする

const listRootElm = document.getElementById('container');
listRootElm.addEventListener('click', (e) => {
  if(e.target.className === 'btn-completed') {
    // なにか処理がはいります
  }
});
itsumoonazicodeitsumoonazicode

でもこうすると、たとえば下記のようなHTMLを前提にすると、spanで囲んだ要素をクリックしてもイベントが発生しない。

サンプルHTML
<ul id="container">
  <li>
    <buton class="btn-completed">
      <span class="btn-completed-text">タスクを完了にする</span>
    </buton>
    健康診断の予約をする
  </li>
  <li>
    <buton class="btn-completed">
      <span class="btn-completed-text">タスクを完了にする</span>
    </buton>
    ガス代の支払いをする
  </li>
</ul>
itsumoonazicodeitsumoonazicode

これをボタン(.btn)の範囲内がクリックされたときにifの条件を満たすようにするために、一番最初に載せたリンクを参照して、以下のCSSを追加した。

.btn-completed-text {
  pointer-events: none;
}
itsumoonazicodeitsumoonazicode

解決したいことは解決したので、別にこれでもいいんだけど、
今日改めてイベント移譲(Event Delegation)の記事を見たら、どうもCSSを追加しなくてもいけそうなことを発見したので書き換えようと思う。

https://ja.javascript.info/event-delegation

itsumoonazicodeitsumoonazicode

参考にする該当コードをそのままコピーしてきた

table.onclick = function(event) {
  let td = event.target.closest('td'); // (1)

  if (!td) return; // (2)

  if (!table.contains(td)) return; // (3)

  highlight(td); // (4)
};
itsumoonazicodeitsumoonazicode

イベントが発生した要素の親に、指定要素が含まれているかを確認している

let td = event.target.closest('td');

含まれてなければ処理はそこで終了

if (!td) return;

コンテナー要素をイベント監視の対象にしているので、イベントの発生した要素が、想定したコンテナーに属するのかを確認する

if (!table.contains(td)) return;
itsumoonazicodeitsumoonazicode

これを使えば、pointer-events: none;を指定しなくても良くなる

let td = event.target.closest('td');
このスクラップは2022/05/07にクローズされました