contenteditable卡顿主因是浏览器频繁触发同步layout+paint,而非dom体积;需避免innerhtml读写、禁用焦点动画、虚拟滚动、精简可编辑节点,并针对ios焦点问题延迟focus及避开300ms点击延迟。

contenteditable="true" 为什么一加就卡顿
不是 DOM 太大,而是浏览器在每次输入、粘贴、光标移动时都触发完整 layout + paint,尤其当元素内含大量嵌套标签或 CSS 样式复杂时,帧率会明显掉。更隐蔽的是,innerHTML 赋值、execCommand 调用、甚至监听 input 事件中频繁读取 textContent 都可能触发强制同步重排。
实操建议:
- 避免在
input回调里直接读写el.innerHTML—— 改用el.textContent(纯文本场景)或节流后批量处理 - 禁用不必要的 CSS 动画/过渡:比如
:focus上的box-shadow或transform,它们会在每次焦点切换时重绘 - 对长内容做虚拟滚动:只渲染可视区域内的段落,用
getBoundingClientRect()判断是否在视口内 - 移除所有未使用的
contenteditable子元素 —— 浏览器会对每个可编辑节点维护独立的编辑上下文,数量越多开销越大
移动端 focus 失败导致输入延迟或失焦
iOS Safari 和部分安卓 WebView 对 contenteditable 的焦点管理极不友好:软键盘弹出时容易丢失焦点、点击空白处不响应、快速连续输入后光标“卡住”。这不是代码写错了,是浏览器主动降级了焦点调度优先级。
实操建议:
- 必须显式调用
el.focus()并配合setTimeout延迟 100ms,绕过 iOS 的焦点抑制机制 - 禁止
user-select: none和pointer-events: none—— 这两个 CSS 属性在移动端会让contenteditable彻底失活 - 监听
touchstart后立即el.focus(),不要等click(iOS click 有 300ms 延迟) - 避免在
focus事件里执行耗时操作(如 DOMPurify.sanitize),否则软键盘弹出前会卡住
DOMPurify.sanitize() 在高频 input 下拖慢体验
每敲一个字都跑一遍 DOMPurify.sanitize() 是常见误区。它内部做 HTML 解析 + 标签白名单遍历 + 属性过滤,单次耗时可达 5–20ms,在低配安卓机上极易掉帧。
实操建议:
- 仅在粘贴(
paste)、回车(keydown.enter)、保存(blur)三个节点做 sanitize,而非input - 用正则预筛:先
el.innerHTML.replace(/<script>)<[^<]*)*<\/script>/gi, '')</script>快速剔除 script 标签,再进 DOMPurify - 启用
RETURN_DOM模式并缓存 purified node,避免重复 parse —— 但注意不能直接插入 document,需 cloneNode(true) - 纯文本场景彻底放弃 sanitize,改用
textContent读写,零成本防 XSS
MutationObserver 监听导致无限循环
为监听结构变化而加的 MutationObserver,一旦在回调里修改了 innerHTML,就会再次触发自身 —— 尤其在清洗非法标签、标准化段落(把 <div> 替成 <code><p></p>)时极易陷入死循环。
实操建议:
- observer 启动前设开关:
isMutating = true,回调开头if (isMutating) return,写完再置false - 不用
childList: true监听全部子节点,改为只监听characterData: true+subtree: false,聚焦文本节点变更 - 避免在 observer 中调用
document.execCommand—— 它会触发另一次 DOM 变更,且已被 Chrome 标记为 deprecated - 标准化段落这类逻辑,改用
input+keydown.enter组合拦截,比事后清洗更轻量、更可控











