fix(cdp): fire onChange for React/Vue controlled inputs (#353)
Typing over CDP into a React controlled input never fired onChange, so the value never reached component state (issue #324). Two root causes, both needed: 1. Value tracker. React/Preact/Vue install a value tracker by redefining `value` on the element instance to record the last value they wrote. obscura's CDP typing assigned `el.value = ...` directly, which runs that wrapper and keeps the tracker in sync, so React saw no change on the input event. Synthetic value/backspace writes now go through a new __obscura_setFieldValue helper that writes via the element's prototype setter, bypassing the tracker. The tracker stays stale, so the input event reads as a real edit. 2. Event handler feature detection. React decides whether to use the modern `input` event via `("oninput" in document)`. obscura exposed the GlobalEventHandlers on* attributes only on window, so that check failed and React fell back to a legacy change-detection path that ignores input events. The on* attributes are now also present on Document and Element, matching the HTML spec (GlobalEventHandlers is mixed into Window, Document, and HTMLElement). Verified end to end over CDP against the real React 18 UMD build: typing into controlled inputs updates state, the component re-renders, and a submit handler reads the captured values. Closes #324.
This commit is contained in:
1 parent
b5039a8a7f
commit
08c1f0d721
3 files changed
+108
-11
No files matched your search
@@ -14,12 +14,12 @@ fn insert_text_js(escaped_text: &str) -> String {
|
||||
var v = t.value || '';\
|
||||
var s = t.selectionStart, e = t.selectionEnd;\
|
||||
if (s == null) {{\
|
||||
t.value = v + '{text}';\
|
||||
globalThis.__obscura_setFieldValue(t, 'value', v + '{text}');\
|
||||
}} else {{\
|
||||
s = Math.max(0, Math.min(s, v.length));\
|
||||
e = (e == null) ? s : Math.max(0, Math.min(e, v.length));\
|
||||
var lo = Math.min(s, e), hi = Math.max(s, e);\
|
||||
t.value = v.slice(0, lo) + '{text}' + v.slice(hi);\
|
||||
globalThis.__obscura_setFieldValue(t, 'value', v.slice(0, lo) + '{text}' + v.slice(hi));\
|
||||
var caret = lo + ('{text}').length;\
|
||||
t.setSelectionRange(caret, caret);\
|
||||
}}\
|
||||
@@ -39,16 +39,16 @@ const BACKSPACE_JS: &str = "(function() {\
|
||||
var v = t.value || '';\
|
||||
var s = t.selectionStart, e = t.selectionEnd;\
|
||||
if (s == null) {\
|
||||
t.value = v.slice(0, -1);\
|
||||
globalThis.__obscura_setFieldValue(t, 'value', v.slice(0, -1));\
|
||||
} else {\
|
||||
s = Math.max(0, Math.min(s, v.length));\
|
||||
e = (e == null) ? s : Math.max(0, Math.min(e, v.length));\
|
||||
if (s !== e) {\
|
||||
var lo = Math.min(s, e), hi = Math.max(s, e);\
|
||||
t.value = v.slice(0, lo) + v.slice(hi);\
|
||||
globalThis.__obscura_setFieldValue(t, 'value', v.slice(0, lo) + v.slice(hi));\
|
||||
t.setSelectionRange(lo, lo);\
|
||||
} else if (s > 0) {\
|
||||
t.value = v.slice(0, s - 1) + v.slice(s);\
|
||||
globalThis.__obscura_setFieldValue(t, 'value', v.slice(0, s - 1) + v.slice(s));\
|
||||
t.setSelectionRange(s - 1, s - 1);\
|
||||
}\
|
||||
}\
|
||||
@@ -173,7 +173,7 @@ pub async fn handle(
|
||||
if (!target) return;\
|
||||
target.dispatchEvent(globalThis.__obscura_markTrusted(new KeyboardEvent('keypress', {bubbles:true,key:'Enter',code:'Enter'})));\
|
||||
if (target.localName === 'textarea') {\
|
||||
target.value = (target.value || '') + '\\n';\
|
||||
globalThis.__obscura_setFieldValue(target, 'value', (target.value || '') + '\\n');\
|
||||
target.dispatchEvent(globalThis.__obscura_markTrusted(new Event('input', {bubbles:true})));\
|
||||
} else {\
|
||||
var form = target.form || (target.closest && target.closest('form'));\
|
||||
|
||||
Reference in new issue
Block a user