回车插入是浏览器对contenteditable的默认富文本行为,并非bug;chrome、safari、firefox(60+)和edge均统一用包裹新段落,本质是插入块级容器而非纯换行符。

为什么回车总插入 <div> 而不是换行?<p>这不是 bug,是浏览器对 <code>contenteditable 的默认富文本行为:Chrome、Safari、Firefox(60+)和 Edge 都统一用 <div> 包裹新段落。它本质是“插入块级容器”,不是你想的“按一下回车就加个 <code>\n”。如果你要纯文本换行(比如聊天输入框),这个 <div> 会破坏结构、增加多余标签、干扰样式计算。<h3>用 <code>document.execCommand("defaultParagraphSeparator") 切换段落标签
这是最轻量、兼容性最好的方案,无需重写编辑逻辑:
- 想让回车生成
<p></p>:在编辑器初始化后执行document.execCommand("defaultParagraphSeparator", false, "p") - 想让回车只换行(
<br>):用document.execCommand("defaultParagraphSeparator", false, "br")—— 注意:这仅控制段落分隔符,Shift+Enter仍可能触发<br>,而纯Enter在部分浏览器中仍可能 fallback 到<div> <li>该命令需在编辑器获得焦点后调用才生效;若编辑器动态创建,建议在 <code>focus事件里再执行一次 - 已废弃但实际支持极广(IE11 到 Chrome 127、Safari 17、Firefox 128 均可用)
- 监听
keydown,检测event.key === 'Enter'且!event.shiftKey - 立即
event.preventDefault(),阻止浏览器默认插入<div><li>用 <code>element.innerText取值(避免残留<div> 或 <code><p></p>标签) - 若需保留换行视觉效果,给容器加 CSS:
white-space: pre-wrap,这样\n会被渲染为换行 - 不要用
innerHTML回填,否则可能重新引入标签;改用textContent = cleanedText+ 手动恢复光标位置(用Selection+Range) - iOS Safari 下,
execCommand对"br"支持不稳定,更推荐拦截 +white-space: pre-wrap方案 - 用户粘贴 Word 或网页内容时,会带入
<div>、<code><span style="..."></span>甚至<script></script>—— 即使你禁了 Enter,也要在input或paste事件里做 HTML 清洗 -
compositionend必须监听,否则中文输入法上屏后可能多出一个空<div> <li>Android Chrome 的 <code>input事件在组合输入期间不触发,得靠compositionend+setTimeout(() => { /* read value */ }, 0)来兜底
监听 keydown 拦截 Enter 并手动处理换行
当你要完全掌控行为(比如保存前清空格式、或只允许单行),就得自己接管回车:
移动端和粘贴带来的额外坑
桌面端处理完 Enter,不代表问题结束:
真正麻烦的从来不是怎么让回车不插 <div>,而是你一旦接管了 Enter,就得连带管住粘贴、输入法、移动端焦点、光标定位——这些点漏掉任何一个,都会在灰度期突然冒出来。</div>











