contenteditable卡顿主因是浏览器高频触发layout/paint,需禁用:focus动画、用textContent替代innerHTML、虚拟滚动、减少可编辑节点;iOS焦点抑制需setTimeout绕过;DOMPurify仅在paste/enter/blur时调用。

contenteditable="true" 一加就卡,不是 DOM 太大,是 layout + paint 被高频触发
浏览器在每次输入、粘贴、光标移动时,都会对 contenteditable 元素执行完整同步重排(layout)和重绘(paint),尤其当内部有嵌套标签或复杂 CSS 时,帧率立刻掉。这不是代码写得“重”,而是浏览器行为本身开销高。
- 避免在
input事件回调里读写el.innerHTML—— 改用el.textContent(纯文本场景)或节流后批量处理 - 禁用所有不必要的
:focus动画:比如box-shadow、transform,它们会在每次焦点切换时强制重绘 - 对长内容做虚拟滚动:只渲染可视区域内的段落,用
getBoundingClientRect()判断是否在视口内 - 移除所有未使用的
contenteditable子元素 —— 每个可编辑节点都维护独立编辑上下文,数量越多,主线程压力越大
移动端 focus 失败不是 bug,是 iOS Safari 主动降级了调度优先级
iOS Safari 和部分安卓 WebView 在软键盘弹出时,会抑制 focus() 调用,导致点击无响应、光标卡住、连续输入后失焦。这不是你漏写了事件监听,而是浏览器策略层面的限制。
- 必须显式调用
el.focus()并包裹setTimeout(() => el.focus(), 100),绕过焦点抑制机制 - 禁止使用
user-select: none和pointer-events: none—— 这两个 CSS 属性会让contenteditable在移动端彻底失活 - 监听
touchstart后立即focus(),不要等click(iOS 的click有 300ms 延迟) - 避免在
focus事件里执行耗时操作(如DOMPurify.sanitize()),否则软键盘弹出前会卡住
DOMPurify.sanitize() 每敲一个字都跑一遍,等于主动制造掉帧
DOMPurify.sanitize() 内部要做 HTML 解析 + 白名单遍历 + 属性过滤,单次耗时可达 5–20ms。在低配安卓机上,高频 input 下极易掉帧,甚至阻塞主线程。
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
- 仅在三个节点做 sanitize:
paste、keydown.enter、blur,而非每次input - 粘贴前先用正则预筛:
el.innerHTML.replace(/<script>/gi, '')</script>快速剔除 script 标签,再进DOMPurify - 启用
RETURN_DOM模式并缓存 purified node,但插入前必须cloneNode(true),不能直接插入 document - 纯文本场景彻底放弃
sanitize,改用textContent读写 —— 零成本防 XSS
没加 tabindex="0",等于 contenteditable 没通电
contenteditable="true" 只提供编辑能力入口,不赋予聚焦能力。div、p 等非表单元素默认无法被 Tab 键聚焦,el.focus() 也不生效,document.execCommand 全部静默 —— 因为压根没有焦点,Selection API 拿不到 range。
立即学习“前端免费学习笔记(深入)”;
- 必须显式加上
tabindex="0",这是所有后续交互(加粗、换行、粘贴拦截)的前提 -
tabindex="-1"只支持 JS 主动聚焦,无法进 Tab 链,不适合用户自主操作场景 - 某些旧版 Safari 对
tabindex缺失更敏感,光标点击后可能瞬间消失 - 若父容器有
overflow: hidden且内容溢出,tabindex="0"有时会触发滚动跳变,建议加scroll-behavior: smooth缓解
contenteditable 在真实设备上运行时必然面对的底层约束。绕开它们靠的不是更“高级”的封装,而是对浏览器渲染管线和焦点调度机制的诚实认知。


















