HTML仕様の落とし穴:`??=` から `||=` への修正で要素IDの自動生成が正しく機能するように

basecamp/lexxy

lexxy-editor カスタム要素の ID 自動生成が、HTML の element.id の仕様に起因するバグにより機能していなかった問題を修正しました。??=(nullish 代入)を ||=(論理 OR 代入)に変えるだけで、意図通りの動作が実現されています。

背景

HTML 仕様では、element.id は未設定の場合に nullundefined ではなく、空文字列 "" を返します。JavaScript の nullish 代入演算子 ??= は左辺が null または undefined のときだけ右辺を評価するため、"" に対しては代入が行われません。結果として、id 属性が設定されていない lexxy-editor 要素は自動生成 ID を受け取れず、空のまま残る状態が続いていました。

この問題は、コンポーネントの connectedCallback における一行の演算子選択に起因するものです。Web Components の開発では element.id"" を返すという挙動は見落とされやすく、今回のケースはその典型例といえます。

技術的な変更

修正は src/elements/editor.jsconnectedCallback 内の1行のみです。

変更前:

this.id ??= generateDomId("lexxy-editor")

変更後:

this.id ||= generateDomId("lexxy-editor")

||=(論理 OR 代入) は左辺が falsy(nullundefined""0false など)のときに右辺を代入するため、element.id が空文字列であるケースも正しく処理できます。"my-custom-id" のように明示的な ID が設定されている場合は truthy なので代入はスキップされ、既存の ID は保持されます。

合わせて test/browser/tests/editor/id_generation.test.js が新規追加され、以下の4つのシナリオが Playwright でカバーされています:

  • ID 未設定時に lexxy-editor- プレフィックスの ID が自動生成されること
  • コンテンツ要素の ID がエディタ ID から ${editorId}-content の形式で派生すること
  • 複数の lexxy-editor が互いに異なる一意の ID を持つこと
  • 明示的に設定された ID が上書きされずに保持されること

設計判断

??= ではなく ||= を選択する判断は、HTML の element.id が空文字列を返すという仕様への対応として最小限かつ正確なアプローチです。

??= から ||= への変更は、意味的には「値が存在しない場合」から「値が有効でない場合」への条件の拡張です。ID の文脈では空文字列は無効な値であるため、この拡張は適切です。なお、この変更により id = "" のように明示的に空文字列が設定された場合もIDが自動生成されるようになります。これは、空の ID を意図的に利用する特殊なケースを除き、ほとんどの状況で望ましい挙動といえます。

テストを修正と同時に追加することで、同様の退行を防ぐ安全網が整備されています。

まとめ

??=||= の1文字の違いが、HTML の element.id 仕様との相互作用により要素 ID の自動生成を完全に無効化していた問題を解消しました。演算子の選択が DOM API の返値仕様と噛み合わないと、コードは構文的に正しくても意図した動作をしないというケースを端的に示す変更です。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
90676b2a

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

品質レビュー結果

Review Status:
リトライ後承認
Review Count:
2回 (改善を経て承認)
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

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

リード文(総論)、背景・技術詳細・設計判断(各論)、まとめ(結論)の3部構成が明確に守られています。各セクションの役割が明確で、非常に分かりやすい構成です。

カスタムMarkdown構文 ✓ PASS

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

ファイル名付きシンタックスハイライト(```javascript:src/elements/editor.js)とPR番号のリンク記法([PR #933](URL))が正しく使用されています。

対象読者への適合性 ✓ PASS

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

??=と||=の違いやelement.idの挙動など、専門的なトピックを扱っており、対象読者であるエンジニアに適した技術レベルと表現です。

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

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

各セクションが総論→各論の構成になっており、各段落がトピックセンテンスで始まっているため、非常に読みやすいです。1段落1トピックの原則も守られています。

Diff内容との照合 ✓ PASS

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

記事内のコード引用は、提供されたDiffの内容(this.id ??= から this.id ||= への変更)と完全に一致しています。テストファイルの追加とその内容の要約も正確です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「nullish 代入」「論理 OR 代入」「falsy」「connectedCallback」といった技術用語が正確かつ適切な文脈で使用されています。

説明の技術的正確性 ✓ PASS

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

element.idが空文字列を返す仕様と、それに伴う??=と||=の挙動の違いに関する説明は技術的に正確で、PRの変更意図を的確に解説しています。

事実の突合 ✓ PASS

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

記事内のすべての主張は、PRのDescriptionやDiffの内容によって裏付けられています。特に、問題の原因と解決策がPR情報と完全に一致しており、ハルシネーションは見られません。

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

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

PR番号(#933)やファイルパス(src/elements/editor.jsなど)が正確に記載されています。

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

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

記事のタイトルは、PRの主題「要素IDの生成」を的確に表現しつつ、「HTML仕様の落とし穴」という観点を加えており、読者の興味を引く優れたタイトルです。

外部知識の正確性 ✓ PASS

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

PR情報に記載のない、バージョンサポート状況やリリース日程などの外部知識の捏造はありません。

時間表現の正確性 ✓ PASS

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

記事内の時間表現(「修正しました」など)は、完了した変更を報告する文脈として適切であり、PR情報との矛盾はありません。