HUD 用 data 屬性加 closest 阻擋畫布事件

畫布上疊 UI(工具列、屬性面板、HUD)時,在 UI 根元素加 data-hud 標記,畫布的事件 handler 用 e.target.closest('[data-hud]') 判斷是否落在 UI 上、是的話直接 return

做法

// 畫布 handler
const onPointerDown = (e) => {
  if ((e.target as HTMLElement).closest('[data-hud]')) return  // 點到 UI 就略過
  // 正常畫布邏輯...
}
 
// UI 元件
<div className="toolbar" data-hud onPointerDown={(e) => e.stopPropagation()}>
  ...
</div>

為什麼比 stopPropagation 更好

只用 stopPropagation

  • 必須在「每個」UI 元素都記得加 — 漏掉一個就破功
  • 第三方元件無法控制(例如 Headless UI、Radix 的內部結構)
  • 父層其他 listener(如 analytics)也會被連帶擋掉

data-hud + closest

  • 只在 UI 根元素標記一次,所有後代自動受惠
  • 判斷邏輯集中在畫布 handler 一處,易維護
  • 想擴充新的 HUD 元件只要加 data-hud 屬性

兩者搭配最穩:UI 根加 data-hud 防 capture 階段、UI 根再加 stopPropagation 防 bubble 階段。

為什麼用 data-* 而非 className

  • data-hud 是宣告式語意(「我是 HUD」),不是樣式
  • CSS selector [data-hud]closest 都直接支援
  • 改 className 不會誤傷判斷邏輯(CSS rename 不影響事件流)

適用場景

  • 無限畫布上的工具列、屬性面板、HUD
  • 地圖元件上疊控制按鈕
  • 任何「下層元素有全域事件 handler,上層需要正常互動」的情境

相關