自作のpointerステートマシンは、右クリック等非主ボタンを後から取り消すのではなく pointerdown 入口で拒否する
ドラッグ/クリック/ダブルクリックを自前で判別する自作のpointerイベントステートマシン(pointerdownで状態を開始しpointerupで確定する方式)で、e.button(主ボタンか右ボタンか)をチェックせずに全ボタンを同じ経路に通すと、右クリック(contextmenu)で開くことを意図した操作が、左クリック用の副作用(例: クリックで新規作成)を両方発火させてしまうことがある。contextmenuハンドラ側で副作用を取り消す実装(例: 予約済みタイマーをclear)をしても、pointerdown→pointerup→contextmenuの発火順序はブラウザ/OS/入力方式(マウス右ボタン vs トラックパッドの二指タップ等)で保証がなく、取り消しが副作用の発火に間に合わないレースコンディションが残る。
適用条件: 同じ要素上のポインター操作が、ボタン種別(左/右)で完全に別の意味を持つ(例: 左クリック=新規作成、右クリック=メニュー表示)自作pointer状態機械を実装する場合。
対処方法: 副作用を事後に取り消そうとせず、pointerdownハンドラの先頭でe.button !== 0(タッチ入力は除外)なら即座にreturnし、そのポインターセッションは状態機械に一切入らないようにする。入口で拒否すれば、後続のpointermove/pointerupハンドラも一切登録されず、副作用予約自体が発生しないため、別ハンドラとのイベント順序に依存しない。
確認方法: 対象要素を右クリックし、副作用用の遅延時間(例: ダブルクリック判別の献予時間)を超えて待機した後も、副作用(新規作成等)が一切発生していないことを自動テストで確認する。並行して、左クリックの既存振る舞いが影響を受けていないことも確認すれば、回帰を防げる。
教訓: 「別ハンドラで副作用を取り消せば十分」という発想は、イベント発火順序が仕様上保証されていないキー/ポインターイベント群ではレースを勝ちに行くだけで本質的な修正にならない。発生源(入口)で条件分岐して経路自体に入らせない方が、レースの可能性を構造的に消す。