Prevent spurious `lexxy:change` event on initial load
Lexxy now prevents the lexxy:change event from firing when the editor loads its initial value that is re‑serialized or sanitized, by initializing the internal form value from the serialized editor content.
背景
lexxy:change は内部フォーム値が変化したときにディスパッチされますが、初期ロード時にサニタイズ前の HTML が #setInternalFormValue に渡されると、サニタイズ後の値との差分が検出されて不要なイベントが発火していました。特に、<del> が <s> に正規化されたり、<script> タグが除去されたケースで顕在化します。これにより、ページロード直後に期待しない lexxy:change がリスナーへ届き、フォームの状態管理が乱れる可能性がありました。PR #1095 はこの誤検知を根本的に解消することを目的としています。
技術的な変更
新しいヘルパー #readSanitizedEditorValue
変更点: src/elements/editor.js にプライベートメソッド #readSanitizedEditorValue を追加し、エディタからサニタイズされた HTML を取得して null 可能な形で返します。
#readSanitizedEditorValue(editor = this.editor) {
return editor?.read(() => {
return sanitize($generateHtmlFromNodes(this.editor, null))
}) ?? null
}
このメソッドは従来 value ゲッタで直接呼び出していたロジックを抽象化し、テストやコールバックからも再利用できるようにしました。
value ゲッタの単純化
変更前は cachedValue を取得する際にインラインで sanitize($generateHtmlFromNodes…) を実行していましたが、変更後は #readSanitizedEditorValue に委譲するだけになりました。
- return this.cachedValue ??= this.editor?.read(() => {
- return sanitize($generateHtmlFromNodes(this.editor, null))
- }) ?? null
+ return this.cachedValue ??= this.#readSanitizedEditorValue()
この変更はロジックの重複排除と可読性向上を目的としています。
初期化フローの再構成
$initialEditorState コールバック内でサニタイズ設定を先に行い、続いて初期値をロードし、最後に内部フォーム値をサニタイズされた結果で上書きします。#configureSanitizer の呼び出し位置を従来の #createEditor から移動し、早すぎても遅すぎても正しく適用されないというタイミング問題を解消しました。
- $initialEditorState: (editor) => this.#loadInitialValue(editor)
+ $initialEditorState: (editor) => {
+ this.#configureSanitizer(editor)
+ this.#loadInitialValue(editor)
+ this.#setInternalFormValue(this.#readSanitizedEditorValue(editor))
+ },
この手順により、エディタが生成したノードはサニタイズ済みの HTML に変換され、#setInternalFormValue が呼ばれても既に正規化された値になるため、変更検知が走りません。
テストの追加
test/browser/fixtures/initial-value-renormalized.html と initial-value-sanitized.html を新規作成し、lexxy:change が初期ロード時に 0 件であることを検証するテストケースを events.test.js に追加しました。これにより、正規化やサニタイズが行われたシナリオでも期待通りイベントが抑制されることを自動的に保証します。
設計判断
この変更は 「初期値はサニタイズ済みのシリアライズ結果で内部状態を初期化する」 という設計方針を採用しました。サニタイズ設定はエディタ生成直後ではなく、実際にノードが生成される直前に実行することで、必ず _htmlImports が利用可能な状態で適用できます。さらに、内部フォーム値の設定ロジックは 「変更があった場合にだけ lexxy:change を発火」 という既存の振る舞いを保持しつつ、初期化時の余計な差分検出を防ぐ形で拡張されています。結果として、既存のカスタムリスナーやフォーム送信ロジックに対する後方互換性は維持され、イベントノイズの削減という明確な価値が得られました。
まとめ
PR #1095 は、エディタの初期化プロセスを見直し、サニタイズ済みのシリアライズ結果を内部フォーム値として設定することで、不要な lexxy:change イベントの発火を防止します。新しいヘルパーメソッドとコールバックの再構成により、ロジックがシンプルかつテスト可能となり、既存の機能や互換性に影響を与えずに信頼性が向上しました。