HTML/CSSでUIを構築する際の難点は、UI部品に設定する値の管理です。 古典的な方法は、DOMの属性に書いておき、eventオブジェクトから取り出す次のような方式です。
<button id="ui-btn" data-value="1">Button</button>
<script>
document.querySelector('#ui-btn').addEventListener(
'click',
(event) => { console.log(event.target.dataset.value); }
);
</script>
この例では、要素のdata-value="1"をイベントハンドラのevent.targetからアクセスしています。
しかし
targetプロパティ
は次のようにHTMLが変化すると壊れます。
<button id="ui-btn" data-value="1">
<i class="icon">send</i> <span>Button</span>
</button>
この例では、event.targetが指すDOMは、button, i, spanのいずれかで、ユーザーがクリックした場所に依存します。
iやspanが反応したケースでは.dataset.valueには値が入りません。
currentTargetプロパティ
を用いてevent.currentTargetのようにアクセスすると、addEventListener()を取り付けたDOMを指すようになり操作による参照の揺れをなくせます。
targetプロパティ禁止令
DOMに値を置く場合、おそらくtargetを排してcurrentTargetに一本化しないと予期せぬトラブルが起きやすくなります。
次のような理由から、この2つは用途に応じて使いわけるものではないと言えます。
- UIの一番広い面は、その最も外側のタグが該当する
- 一番外側にイベントハンドラを設定することで見た目と挙動が一致する
- currentはeventバブルの起点ではないため、
currentTargetの指している対象はさほど自明ではない。targetと混在すると意味がとりづらくなる
冒頭の例では、内部にDOMツリーが成長してずれていくケースを挙げましたが、じっさいにはコードを何も変更することなく挙動が変わる例に遭遇しました。
そのケースは最終的にどのような変化が起きたのかを特定できていません。類型としてCSSのz-indexが変わったことによりイベントバブルの起点が変わるようなケースを想定できます。
多くの実装ではHTMLテンプレートとハンドラは分離するので、テンプレートを書きかえる際にハンドラがどのような影響を受けるのかは自明ではありません。 targetプロパティは変更そのものに弱いため、外部ライブラリが提供するUIコンポーネントの変化にも脆くなりますし、CSSのグローバルな修飾の影響も受けます。
そして、この壊れ方はE2Eテストでもほぼ検出できません。人間には1px角のエリアは押せませんが、テストコードは非現実的な精度で押せてしまうからです。
Javascriptに値を置く方法もあるがリーク懸念あり
ここまでのDOMに値を置く方法の他に、イベントハンドラのクロージャに値を置く方法が使える場合もあります。
この方法は、DOMが指すものの乖離の影響を受けない点では優れています。 ただし、関数コンポーネントのオブジェクトはコードの見かけほどピュアではないことが多く、値のライフサイクルがUIの見た目と一致しないことがある点には注意が必要です。
これは使用しているUIフレームワークの品質にかなりよります。Litは見た目と連動してかなり堅牢であるため、クロージャに値を置く方法は併用できます。
本質的にはDOMとJavascriptの乖離抑制が必要
currentTargetはイベントハンドラのあるDOMと一致する点が重要です。
冒頭の例のようにHTMLは外側にも内側にも成長する言語であるため、ある時点における内外の関係を維持できる保証はありません。
唯一変わらないのは該当DOMそのものです。
また、HTMLとJavascriptのコードが分離していることも、乖離に対して脆い理由です。
このチェックをlintで検出する方法もありますが、より本質的にはUIフレームワークが一貫したDOMの値を指すショートハンドの構文が必要です。
2026年現在、その機能を提供するフレームワークはないので、DOMアクセスの方法を統一することがワークアラウンドになります。