PopoverのDOM移動時にイベントハンドラを維持

shoelace-style/webawesome

Web AwesomeのPopoverコンポーネントが appendChild などでDOM内を移動すると、AbortController のシグナルが中断されてイベントリスナーが失われる問題が修正されました。この変更により、要素が再接続された際にイベントハンドラが適切に再確立されるようになります。

背景

Popoverコンポーネントの親コンテナをDOM内で移動すると、アンカー要素のトリガーイベントや閉じる動作が機能しなくなる問題が報告されていました。#1963 の報告では、以下のような状況で問題が発生していました:

  • Popoverの親要素を appendChild で別の場所に移動
  • 移動後、アンカー要素をクリックしてもPopoverが開かない
  • 既に開いているPopoverを閉じる操作も機能しない

この問題は、disconnectedCallback 内で AbortController のシグナルが中断されることが原因でした。Web ComponentsのライフサイクルではDOM移動時に disconnectedCallbackconnectedCallback が順次呼び出されますが、中断されたシグナルに紐付いたイベントリスナーはそのまま失われていました。

技術的な変更

packages/webawesome/src/components/popover/popover.tsconnectedCallback メソッドに、2つの復旧処理が追加されました。

変更後:

connectedCallback() {
  super.connectedCallback();
  if (!this.id) {
    this.id = uniqueId('wa-popover-');
  }

  // Recreate event controller if it was aborted
  if (this.eventController.signal.aborted) {
    this.eventController = new AbortController();
  }

  // Re-establish anchor connection after being moved in the DOM
  if (this.for && this.anchor) {
    this.anchor = null; // force reattach
    this.handleForChange();
  }
}

最初の処理は イベントコントローラの再作成 です。eventController.signal.aborted をチェックし、中断されていれば新しい AbortController インスタンスを生成します。これにより、新しいシグナルでイベントリスナーを再登録できるようになります。

次の処理は アンカー接続の再確立 です。for プロパティとアンカー要素が存在する場合、意図的に anchornull にリセットしてから handleForChange を実行します。この強制的なリセットにより、handleForChange 内の処理が確実に実行され、新しいシグナルでイベントリスナーが再登録されます。

設計判断

この修正は wa-tooltip コンポーネントで既に採用されているパターンに従っています。

PR内のコメントで言及されているように、Tooltipコンポーネントでも同様のDOM移動問題が存在し、同じアプローチで解決されていました。Popoverの実装もTooltipと類似の構造を持つため、実証済みの解決策を適用することで、一貫性のある動作とメンテナンス性の向上を実現しています。

anchor の明示的なリセットは、単に handleForChange を呼び出すだけでは不十分なケースに対応するための措置です。既存のアンカー参照が残っている場合、handleForChange 内の差分検出が変更なしと判断してイベントリスナーの再登録をスキップする可能性があります。null への代入により、確実に「新規接続」として処理されます。

本修正は、Web Componentsのライフサイクルに沿った最小限の変更で、既存の動作に影響を与えることなくDOM移動のシナリオをサポートしています。

記事メタデータ

Generated by:
Claude Sonnet 4.5 for DiffDaily

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

「リード文(総論)→背景・技術的な変更・設計判断(各論)→まとめ(結論)」の構成が明確です。特に「設計判断」セクションが結論としての役割も果たしており、リード文の単なる繰り返しになっていない点が優れています。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きシンタックスハイライト(```typescript:filepath)およびGitHubのIssue/PRへのリンク記法([#1963](URL))が正しく使用されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

Web Componentsのライフサイクル(connectedCallback/disconnectedCallback)やAbortControllerに関する知識を前提としており、専門知識を持つエンジニアという対象読者に適合しています。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションが総論→各論の構成になっており、各段落もトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が遵守されています。可読性が非常に高いです。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事内のコードブロックは、提供されたDiffで追加された内容を正確に反映しています。ファイルパスも`packages/webawesome/src/components/popover/popover.ts`と一致しており、正確です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「AbortController」「connectedCallback」「disconnectedCallback」「アンカー要素」などの技術用語が、PRの文脈に沿って正確に使用されています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

DOM移動時に`disconnectedCallback`で`AbortController`が中断される問題点と、`connectedCallback`でそれを再生成するという解決策の説明は、技術的に正確かつ論理的です。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事内のすべての主張(問題の根本原因、解決策、`wa-tooltip`との関連性など)は、PRのDescriptionやDiffの内容によって裏付けられており、ハルシネーションは検出されませんでした。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#1976)および関連するIssue番号(#1963)が正確に記載されています。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事のタイトル「PopoverのDOM移動時にイベントハンドラを維持」は、PRのタイトル「fix(popover): maintain event handlers when moved in DOM」の内容を正確に反映しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

PR情報に含まれない外部知識(バージョンのサポート状況、リリース日程など)の追加はなく、提供された情報に忠実です。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

「問題が報告されていました」「修正されました」など、過去の問題と完了した修正という時間関係が正確に表現されています。