必须拦截 paste 事件并用 DOMParser 解析 text/html,preventDefault 后白名单过滤标签、清除 style/class,再按光标位置插入清洗文本,禁用正则删标签和 execCommand。

必须拦截 paste 事件,否则浏览器默认原样插入
直接设 contenteditable="true" 后用户 Ctrl+V,浏览器会把剪贴板里的 text/html 原封不动插进来——Word、微信、Outlook 粘的全是带 style、font、span 嵌套甚至 <script> 的 HTML。这不是 bug,是规范行为。
不拦截 paste,后续所有清洗都晚了:DOM 已污染,XSS 风险已引入,段落塌陷已发生。
- 在目标元素上绑定
addEventListener('paste', handler),别绑在document或父容器上 - handler 开头第一行必须是
event.preventDefault(),不能放在异步回调里 - 不要用
onpaste属性写法,它在 Shadow DOM 或动态渲染中不可靠
优先解析 text/html,而不是依赖 text/plain
event.clipboardData.getData('text/plain') 在 Safari、iOS Notes、企业微信等场景下常为空或丢换行,还混入 \u200b(零宽空格)导致段落视觉塌陷。而 text/html 虽然兼容性稍弱(旧版 Safari 返回空),但它是唯一能保留语义结构(如 <p>、<li>)的数据源。
- 先尝试
event.clipboardData.getData('text/html'),哪怕只用来做 DOM 解析中间载体 - 若为空或解析失败,再 fallback 到
text/plain,并立即执行.replace(/\u200b/g, '').replace(/\u00ad/g, '') - 绝对不用
innerText提取内容——它会把<p>的换行语义全吃掉
用 DOMParser + 白名单过滤,别信正则删标签
用 /<[^>]*>/g 正则删 HTML 标签,漏掉注释、CDATA、自闭合标签里的危险属性(比如 <img src=x onerror=alert(1)>),也无法识别语义层级。DOMParser 是目前唯一能安全还原结构并逐节点控制的方式。
使用 Puppeteer + Chrome 将 HTML 渲染为中文 PDF,自动处理图表等待、Tab 展开、动画、测高、白边消除、防分页,适用于看板、报表、网页和交互图表转 PDF。
立即学习“前端免费学习笔记(深入)”;
- 创建
new DOMParser().parseFromString(html, 'text/html')得到文档片段 - 只保留语义明确、样式中立的标签:
p、br、strong、em、ul、ol、li、a(仅保留href) - 对每个保留节点调用
removeAttribute('style')和removeAttribute('class') - 遍历
p或div[display: block]节点,在其后手动补\n,避免段落合并
插入时必须精准落到光标位置,别用 execCommand
document.execCommand('insertText') 在 Chrome 97+、Firefox 84+ 已废弃,Safari 完全不支持;它无视当前光标位置,总把文字插到末尾——用户在段中粘贴,结果文字跑到结尾,体验极差。
- 用
getSelection().getRangeAt(0)获取当前选区 - 调用
range.deleteContents()清空选区(兼容 IE11) - 用
range.insertNode(document.createTextNode(cleanText))插入清洗后文本 - 如果是
textarea场景,改用setRangeText(),IE10+ 全兼容
缩进和换行比去样式更难保全:很多清洗逻辑把全角空格、Tab、首行缩进当“冗余”删掉,结果段落视觉塌陷——这些细节必须在 DOMParser 遍历阶段手动重建,不能靠最后转纯文本兜底。


















