Playwright 黑魔法:用 ClipboardEvent 绕过 React 富文本编辑器注入内容
做 Web 自动化(Playwright / Puppeteer)时,最头疼的问题之一就是操作 React 生态的富文本编辑器。知乎的 Draft.js、字节的 Slate、各种 contentEditable 实现,它们根本不认 DOM 直接修改。
问题在哪?
很多新手会这样写:
python
page.evaluate('document.querySelector(".editor").innerHTML = "hello"')
编辑器毫无反应。为什么?因为 React 管理着自己的虚拟 DOM,你直接操作真实 DOM 它感知不到。编辑器内部的 onChange / onInput 事件根本没有触发。
核心解法:ClipboardEvent('paste')
这些编辑器普遍监听了粘贴事件来接收外部内容。我们可以利用这一点,模拟一次粘贴操作,让编辑器以为用户从剪贴板粘贴了内容。
思路如下:
- 创建一个
DataTransfer对象,放入 HTML 内容 - 构造一个
ClipboardEvent('paste')事件 - 直接在编辑器 DOM 元素上
dispatchEvent
核心代码
javascript
const dt = new DataTransfer();
dt.setData('text/html', htmlContent); // HTML 格式
dt.setData('text/plain', plainTextContent); // 纯文本兜底
const pasteEvent = new ClipboardEvent('paste', {
bubbles: true,
cancelable: true,
clipboardData: dt
});
document.querySelector('.editor').dispatchEvent(pasteEvent);
为什么这招好用?
- 框架无关 --- 不管是 Draft.js、Slate、ProseMirror 还是 Tiptap,都监听 paste 事件
- 触发完整生命周期 --- 编辑器的内容变更检测、字数统计、自动保存都会被正确触发
- 纯前端操作 --- 不需要剪贴板权限,没有安全限制
Playwright 中的完整封装
python
def inject_via_paste(page, selector, html_content):
page.wait_for_selector(selector, state="visible")
page.focus(selector)
page.evaluate("""({sel, html}) => {
const el = document.querySelector(sel);
const dt = new DataTransfer();
dt.setData('text/html', html);
dt.setData('text/plain', html.replace(/<[^>]+>/g, ''));
const event = new ClipboardEvent('paste', {
bubbles: true, cancelable: true, clipboardData: dt
});
el.dispatchEvent(event);
}""", {"sel": selector, "html": html_content})
注意事项
- 内容中如果有图片,需要优先上传到图床再替换为 CDN 链接
- 部分编辑器会弹出"检测到 Markdown 格式,是否解析?"的确认框,需要额外处理
- 对于纯文本编辑器(如小红书),将
text/html换成text/plain即可
总结
与其跟 React 的虚拟 DOM 死磕,不如换个思路------模拟用户行为,让编辑器自己处理内容。ClipboardEvent 方案在我的实战中已经稳定运行了知乎、掘金等多个平台的自动发布流程,是目前绕过富文本编辑器最可靠的方式。