富文本编辑器自动填充:为什么 DOM 里有字,提交却提示正文为空
现象
用脚本给在线博客编辑器填内容:页面上明明能看到完整的标题、段落和小标题,字数统计却停在 0;点「发表」后接口返回「正文内容不能为空」。刷新重填,问题依旧。
这类问题在富文本与 Markdown 编辑器上很常见,根源不在网络或接口,而在写入方式没有同步编辑器的内部状态。
根因:DOM 只是状态模型的投影
早期文本框直接读 textarea.value,改 DOM 等价于改数据。但现代编辑器(TinyMCE、CodeMirror 6、ProseMirror、Lexical 等)都在内存里维护一份独立的文档模型:编辑操作先改模型,再由渲染层把模型投影到 DOM。
因此 element.innerHTML = html 或 el.innerText = text 只改了投影,模型仍是空的。提交时序列化的是模型,后端就会收到空正文。更隐蔽的是字数统计、草稿保存、发布按钮的可用状态也都读模型,于是出现「界面有字、统计为 0、按钮置灰」的组合现象。
三种可行的写入方式
1. 编辑器官方 API(首选)
能拿到实例就用实例方法,例如 TinyMCE 的 editor.setContent(html)、CodeMirror 6 的 view.dispatch({changes})。这类 API 会同时更新模型和视图并触发 change 事件,最稳。
const ed = tinymce.activeEditor;
ed.setContent(html);
console.log(ed.getContent().length); // 写入后校验
前提是页面把实例暴露在全局;若被封装进闭包,就退到下一种。
2. 走浏览器编辑命令
document.execCommand('insertHTML', false, html) 或 CDP 的 Input.insertText,本质是通过浏览器编辑管道插入内容,会派发 beforeinput / input 事件。多数编辑器靠监听这些事件同步模型,兼容性好且速度快。
注意先让编辑区获得焦点;iframe 型编辑器要聚焦 iframe 内的 body,否则命令没有作用目标。
3. 真实键盘事件(兜底)
逐字符派发 keydown / keypress / input,行为最接近真人,几乎所有编辑器都认;缺点是长文本耗时明显,需要分批并控制间隔。适合前两种都失效的定制编辑器。
不要这样做
- 直接给 iframe 内的 body 赋 innerHTML,然后指望提交时能读到内容;
- 用普通表单 fill 工具去填 contenteditable——它通常只对 input / textarea 生效;
- 只检查 DOM 里有没有文字,就认为写入成功。
验证要读模型,不要读 DOM
写入后做两件事:调用编辑器 API 读回内容(getContent() 或 state.doc.toString()),以及观察页面字数统计是否从 0 变成实际字数。两者都通过才算真正写入,这一步能在点「发表」之前把问题拦下来。
还有一层:表单的联动门槛
即使正文写入成功,发布流程常还有一道静默校验:免责声明勾选框、文章类型单选、标签等。未满足时「发表」按钮可能不带任何提示地不响应,或者挂着一个表示禁用的 class。勾选这类自定义控件时,JS 的 element.click() 有时不触发框架监听,需要派发真实鼠标事件;勾完再确认按钮的禁用 class 是否消失,是最直接的判据。
小结
自动化填充富文本,优先级是官方 API > 编辑命令 > 真实键盘;写入后用编辑器自身的状态读回校验,而不是看 DOM;最后把表单的联动门槛当作独立步骤逐项确认。把这三件事固化成模板,换任何编辑器都只需要替换第一行代码。
- 点赞
- 收藏
- 关注作者
评论(0)