onpaste不调preventDefault就无法拦截粘贴,必须首行调用;读取clipboardData需在用户手势同步上下文中,否则为空;处理contenteditable需手动解析插入,防图片需检查clipboardData.items MIME类型。

onpaste 里不调 preventDefault 就等于没拦
浏览器对 input 和 textarea 的粘贴行为是“默认插入”,onpaste 只是通知你一声,不主动阻止,富文本照样进。必须第一行就写 e.preventDefault(),否则后续所有清洗逻辑都白搭。
常见错误:在 onpaste 函数里先做判断、再读剪贴板、最后才 preventDefault()——只要延迟哪怕几毫秒(比如进了 setTimeout),浏览器就已经把原始内容插进去了。
- 内联写法要小心:
onpaste="handlePaste(event)"是安全的;但onpaste={() => handlePaste() }在 React 中可能因重渲染丢失绑定 - 别用
document.addEventListener('paste', ...)全局监听,容易误伤contenteditable编辑器或第三方组件 - 移动端 Safari 对
preventDefault()响应有延迟,iOS 16 以下甚至可能忽略,得加兜底
clipboardData.getData('text/plain') 不是万能钥匙
text/plain 看起来最干净,但实际兼容性拉胯:Safari 常返回空字符串,微信/钉钉复制的内容可能混入零宽字符(\u200b)、软连字符(\u00ad)或丢失换行;macOS Notes 复制的纯文本还可能塌成一行。
更稳的做法是优先取 text/html,用 DOMParser 解析后提取结构化文本,再手动补换行、清空格、删零宽符。
立即学习“前端免费学习笔记(深入)”;
- 先判空:
const cd = e.clipboardData || window.clipboardData,IE 只认后者 - 检查类型:
cd.types.includes('text/html')再调cd.getData('text/html'),避免报错 - Safari fallback 到
text/plain后,务必加.replace(/\u200b/g, '').replace(/\u00ad/g, '')
contenteditable 元素粘贴后样式崩了怎么办
直接设 contenteditable="true" 就不管,等于把剪贴板里的 Word HTML、微信 div、甚至残留 <script></script> 全盘接收。不是浏览器 bug,是规范行为——它只管可编辑,不管怎么粘。
真正可控的是:拦截 paste → preventDefault() → 解析清洗 → 手动插入。不能靠 innerText 或正则删标签,前者丢换行,后者漏注释和自闭合标签。
- 用
new DOMParser().parseFromString(html, 'text/html')得到文档片段 - 遍历子节点,只保留
p、br、strong、em、ul、ol、li,其余全移除 - 对保留节点统一
removeAttribute('style')和removeAttribute('class') - 插入时用
getSelection().getRangeAt(0).insertNode(...),比execCommand更可控
图片、base64、拖拽文本怎么一并防住
只拦 text/plain 或 text/html,挡不住图片粘贴。用户 Ctrl+V 一张 PNG,Chrome 可能先触发带 image/png 的 paste,再触发一次带 text/html 的——后者会把图片转成 <img src="data:image/png;base64,..."> 插进去。
所以得双管齐下:用 clipboardData.items 检查 MIME 类型,遇到 image/ 开头就 preventDefault();再加个 setTimeout(() => { ... }, 0) 扫描刚插入的 <img> 标签并移除。
-
clipboardData.items在 iOS 16.4+ / macOS 13.3+ 才稳定,旧版 Safari 需 fallback 到clipboardData.files.length > 0 - 别信
dragover或drop,它们和粘贴无关,拦不住 Ctrl+V - 如果允许文字但禁图片,处理完后可用
document.execCommand('insertText', false, text)插纯文本(已废弃但目前仍有效)
最麻烦的不是写代码,而是不同浏览器对同一操作的事件派发次数、时机、数据可用性的差异。比如 Chrome 可能发两次 paste,Safari 可能根本不触发,Android WebView 可能静默失败——得靠 input 事件兜底检测非法字符,再清空或重置光标位置。



















