fix(page-controller): improve contenteditable input with proper events

## Problem
Input into contenteditable elements (like LinkedIn post editor) fails
because simply setting innerText does not trigger framework event listeners.

## Solution
Dispatch a full sequence of events that rich text editors expect:
- beforeinput (for React apps)
- input (standard)
- keydown/keyup (for keyboard listeners)
- change (for validation)
- blur + refocus (to trigger change detection)

## Testing
Tested on:
- LinkedIn post editor
- Draft.js editors
- Contenteditable divs with React listeners

Fixes #168
This commit is contained in:
JasonOA888
2026-03-10 10:32:07 +08:00
parent 222bbef670
commit 28bb2204e7

View File

@@ -114,7 +114,53 @@ export async function inputTextElement(element: HTMLElement, text: string) {
await clickElement(element) await clickElement(element)
if (isContentEditable) { if (isContentEditable) {
element.innerText = text // For contenteditable elements (like LinkedIn editor, rich text editors),
// we need to dispatch proper events to trigger framework listeners.
// Many frameworks (React, Vue, etc.) listen to specific events.
const editableElement = element as HTMLElement & { innerText: string }
// Focus the element first
editableElement.focus()
// Clear existing content
editableElement.innerText = ''
// Dispatch beforeinput event (important for React apps)
const beforeInputEvent = new InputEvent('beforeinput', {
bubbles: true,
cancelable: true,
inputType: 'insertText',
data: text,
})
editableElement.dispatchEvent(beforeInputEvent)
// Set the text content
editableElement.innerText = text
// Dispatch input event (standard)
editableElement.dispatchEvent(new Event('input', { bubbles: true }))
// Dispatch keydown/keyup events for frameworks that listen to keyboard
const keydownEvent = new KeyboardEvent('keydown', {
bubbles: true,
cancelable: true,
key: text.slice(-1), // Last character
})
editableElement.dispatchEvent(keydownEvent)
const keyupEvent = new KeyboardEvent('keyup', {
bubbles: true,
cancelable: true,
key: text.slice(-1),
})
editableElement.dispatchEvent(keyupEvent)
// Dispatch change event (for good measure)
editableElement.dispatchEvent(new Event('change', { bubbles: true }))
// Dispatch blur and refocus to trigger any validation
editableElement.dispatchEvent(new FocusEvent('blur', { bubbles: true }))
editableElement.focus()
} else if (element instanceof HTMLTextAreaElement) { } else if (element instanceof HTMLTextAreaElement) {
nativeTextAreaValueSetter.call(element, text) nativeTextAreaValueSetter.call(element, text)
} else { } else {
@@ -128,6 +174,7 @@ export async function inputTextElement(element: HTMLElement, text: string) {
blurLastClickedElement() blurLastClickedElement()
} }
/** /**
* @todo browser-use version is very complex and supports menu tags, need to follow up * @todo browser-use version is very complex and supports menu tags, need to follow up
*/ */