输入处理:拦截粘贴,把富文本洗成纯文本
目标
用户从网页/Word 粘贴内容时,会带进一堆杂乱样式和奇怪标签。本节课拦截 paste 事件,取出纯文本,用 Range 手动插入,杜绝脏 HTML。
运行环境:浏览器。运行方式:在 l1 的 index.html 里,把 <script> 换成下面内容,从别处复制一段带格式文字粘贴进编辑器,会发现只进来纯文本。
<script>
const editor = document.getElementById('editor')
editor.addEventListener('paste', (e) => {
e.preventDefault() // 阻止浏览器默认粘贴(默认会带格式)
const text = (e.clipboardData || window.clipboardData).getData('text/plain')
const sel = window.getSelection()
if (!sel.rangeCount) return
const range = sel.getRangeAt(0)
range.deleteContents() // 删掉选中的(若有)
range.insertNode(document.createTextNode(text)) // 插入纯文本节点
range.setStartAfter(range.lastChild) // 把光标移到文本末尾
range.collapse(true)
sel.removeAllRanges()
sel.addRange(range)
})
</script>clipboardData.getData('text/plain') 只拿纯文本;preventDefault 让默认的"带格式粘贴"不发生,脏 HTML 根本进不来。
名词解释
paste 事件:粘贴时触发,事件对象带 clipboardData(剪贴板数据)。preventDefault() 可阻止默认粘贴行为。
clipboardData.getData(type):按 MIME 类型取剪贴板内容,'text/plain' 是纯文本,'text/html' 是带格式 HTML。
受控输入(Controlled Input):不让浏览器自由改 DOM,而是拦截每一次输入、由自己的逻辑决定最终写入什么——这是可控编辑器的核心。
课后练习
练习:改进粘贴处理,让纯文本里的换行 \n 也保留为编辑器里的换行(用 <br> 或 \n 均可,本例用 \n 由 contentEditable 自行处理)。
答案:上面的实现已经逐字插入纯文本,其中包含的 \n 在 contentEditable 中通常会被保留。若想显式转成 <br>,可把 text 按 \n 切分、用 <br> 连接后插入:
const parts = text.split('\n')
const frag = document.createDocumentFragment()
parts.forEach((line, idx) => {
if (idx) frag.appendChild(document.createElement('br'))
frag.appendChild(document.createTextNode(line))
})
range.insertNode(frag)总结
粘贴是富文本编辑器"脏数据"的最大入口。Word/网页复制来的 HTML 充满 style、font、span 和无效标签,直接放进来会污染你的数据模型、还可能带来 XSS。拦截 paste、只取 text/plain、自己决定怎么插入,是把"输入受控"落到实处的范本。这背后是同一句话:浏览器想自由改 DOM,你要接管每一次修改。顺着这个思路,beforeinput 事件能拦截得比 paste/input 更早(在浏览器落 DOM 之前),是做"受控编辑器"的更彻底方案——下一节处理块级格式时就会用到这类思路。