应劫持paste事件并清理html:优先用text/html解析后提取文本并转义换行,fallback时清理零宽字符;禁用execcommand,改用range插入;保留结构需white-space:pre-wrap或转不间断空格。

粘贴富文本时样式乱飞,怎么只留纯文本
HTML5 contenteditable 元素里直接粘贴 Word、微信、网页内容,常带一堆 font、span、style、class,甚至内联 margin、color,根本不是你想要的“用户输入”,而是“样式污染”。核心解法不是拦截粘贴,而是劫持 paste 事件后主动清理。
- 别用
document.execCommand('insertText')—— 已废弃,Chrome 90+ 报错,且无法处理换行和缩进 - 监听
paste事件,调用event.preventDefault(),再从event.clipboardData.getData('text/html')或降级用'text/plain'提取内容 - 若需保留基础结构(如段落、换行),用 DOMParser 解析 HTML 片段,只保留
p、br、div(含display: block的),删掉所有style属性和class
为什么 innerHTML = clipboardData.getData('text/plain') 还是出问题
因为很多应用(尤其是 macOS 上的 Notes、Pages、企业微信)在剪贴板里同时写入了 text/plain 和 text/html,但 text/plain 可能被自动转义、丢失换行、或混入零宽空格(\u200b)、软连字符(\u00ad)。直接赋值会跳过语义,导致段落塌陷成一行。
- 优先尝试
clipboardData.getData('text/html'),哪怕只是拿它做 DOM 解析的中间载体 - 解析后用
node.textContent提取纯文本,再手动把<br>
、<p></p>转成\n,比依赖text/plain更可控 - 注意:Safari 对
clipboardData.getData('text/html')返回空字符串,必须 fallback 到text/plain,并加.replace(/\u200b/g, '')清零宽字符
execCommand 已废,但旧代码还在跑,怎么安全替换
很多老项目还卡在 document.execCommand('insertHTML', false, cleanHtml),这在 Chrome 98+、Edge 104+ 完全失效,控制台报 TypeError: document.execCommand is not a function。不能硬切,得渐进兼容。
- 检测是否支持:
typeof document.execCommand === 'function',不支持就走insertBefore+Range插入逻辑 - 用
getSelection()+getRangeAt(0)获取光标位置,创建DocumentFragment插入清洗后的节点,避免触发重排 - 特别注意
contenteditable在designMode = 'on'下行为不同,这种模式下不要混用execCommand和现代 API
过滤后文字缩进/首行空格消失,怎么保结构又去样式
用户复制带缩进的代码块或合同条款,粘贴后全变左对齐——不是没留空格,是浏览器把多个连续空格合并成一个(CSS white-space: normal 默认行为)。这不是过滤错了,是渲染层吃掉了。
立即学习“前端免费学习笔记(深入)”;
- 清洗 HTML 后,对文本内容做
.replace(/(^|\n)( {2,}|\t)/g, '$1\u00a0\u00a0'),把开头空格转成不间断空格(),浏览器不会合并 - 或者更稳妥:给
contenteditable加 CSSwhite-space: pre-wrap,允许换行和空格保留,同时不强制等宽 - 别用
<pre class="brush:php;toolbar:false;"></pre>包整个编辑区——它会禁用换行符自动折行,破坏移动端体验
真正难的不是“怎么清样式”,而是判断哪些结构该留、哪些该杀。比如用户从 Markdown 预览页复制一段带链接的句子,你删掉 a 标签,就丢了语义;但保留它,又可能带进危险 javascript: href。这类边界,得按业务定规则,没法通用一刀切。











