🧊

ev.composedPath()でClick awayを実装する

Event.composedPath() - Web APIs | MDN

いつの間にこんな便利なやつが・・。

ev.composedPath()

`click`などのイベントが、どういうDOM経路で発生したのかがわかる。

たとえばこういうHTMLのとき。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>composedPath()</title>
</head>
<body>
  <section>
    <div><button>Hi</button></div>
  </section>
</body>
</html>

こういうコードを書いて、

document.querySelector("button").addEventListener("click", (ev) => {
  const path = ev.composedPath();
  for (const target of path) {
    console.log(target.nodeName || "#window");
  }
});

ボタンをクリックすると。

"BUTTON"
"DIV"
"SECTION"
"BODY"
"HTML"
"#document"
"#window"

という感じに。

Click awayする

「特定の要素の外をクリックしたら」っていうやつで、モーダルやメニューを閉じたいときにやるやつ。

`composedPath()`を使えば、

  • `document`に対するすべての`click`イベントを拾って
  • そのイベントの`composedPath()`に、特定の要素が含まれていない場合
  • コールバックを実行する

という風にできる。

const clickAway = (el, onAway) => {
  const onClick = (ev) => {
    const path = ev.composedPath();
    // 含まれてる = 内側 = 何もしない
    if (path.includes(el)) return;
    onAway();
  };
  
  document.addEventListener("click", onClick);
  return () => {
    document.removeEventListener("click", onClick);
  };
};


const $button = document.querySelector("button");
const buttonAway = () => {
  console.log("Outside!");
}

clickAway($button, buttonAway);

って感じ。

この実装、一昔前までは`Element.contains()`とか、もっと前なら`$.closest()`とか、それなりにヘビーなやつを使ってた気がするので、いい時代になったもんだ!