直接加 contenteditable="true" 不足以实现稳定编辑,必须处理三件事:一是设置 white-space: pre-wrap 和 word-break: break-word 并统一焦点样式;二是组合监听 input、compositionend 和 blur 事件,辅以 mutationobserver;三是拦截 paste 事件,用 domparser 白名单过滤 html 或降级为纯文本插入。

直接加 contenteditable="true" 就能编辑?先看这三件事
不能只写 contenteditable="true" 就完事。浏览器默认行为会立刻暴露问题:Chrome 里空 <div> 点击不聚焦、Firefox 回删卡在末尾、Safari 按回车插 <code><div> 而不是换行——这些不是 bug,是规范行为。
<p>实操建议:</p>
<ul>
<li>必须显式设置 <code>white-space: pre-wrap 和 word-break: break-word,否则长文本折行错位、空格被折叠
outline: none 去掉原生焦点框,再用 box-shadow 或边框模拟选中态,避免跨浏览器表现不一致<input> 或 <textarea></textarea> 加这个属性——无效,且 Chrome/Safari 会静默忽略
input 事件监听内容变化为什么总丢数据
只监听 input 事件,等于主动放弃一半修改场景:右键粘贴、Ctrl+Z 撤销、中文输入法上屏、拖拽插入图片,全都不触发。
实操建议:
- 组合监听:
input+compositionend(捕获中文输入完成) +blur(失焦兜底) - 关键节点必须用
MutationObserver监听childList和characterData,尤其当你允许插图、链接等富文本节点时 - 别在
input回调里直接读el.innerHTML——用户粘贴带<script></script>的 HTML 会执行脚本;优先用el.textContent或白名单清洗后的 HTML
粘贴 Word 或网页内容后样式崩塌、XSS 怎么防
用户 Ctrl+V 粘贴,常带 <font></font>、style、嵌套 <span></span>、甚至内联 width/height,轻则布局错乱,重则执行恶意脚本。
实操建议:
- 拦截
paste事件,调用e.preventDefault(),再取e.clipboardData.getData('text/plain')走纯文本路径最稳 - 若需保留粗体/列表等简单格式,用
DOMParser解析'text/html',递归遍历只留<p></p>、<strong></strong>、<ul></ul>/<ol></ol>/<li>,剔除所有style属性和未知标签 - 禁用
document.execCommand('insertHTML', ...)——它不校验,直接注入;改用document.execCommand('insertText', false, text)(虽已废弃,但目前兼容性最好)
React/Vue 动态渲染后光标消失、focus 失效怎么救
组件 re-render、v-html 更新、或手动插入节点后,浏览器常丢失光标位置,甚至把焦点重置到页面顶部——这不是框架问题,是 DOM 替换导致 selection 信息丢失。
实操建议:
- 聚焦前手动保存:在
focus事件里用window.getSelection()记录range.startContainer和startOffset - 渲染完成后立即恢复:用
requestAnimationFrame延迟执行selection.addRange(range),避开 DOM 同步时机 - 移动端必须监听
compositionstart/compositionend,否则拼音输入上屏时内容丢失











