最常见原因是元素不可聚焦,需加tabindex="0";其他失效情况包括父级contenteditable="false"、css禁用user-select/pointer-events、shadow dom未显式设置、内联元素如定位异常。

contenteditable="true" 为什么加了却不能编辑
最常见的情况是元素不可聚焦:原生 <div> 或 <code><p></p> 没有默认焦点能力,必须加 tabindex="0" 才能被 focus() 调用或键盘 Tab 切入。另外,以下任一条件都会让 contenteditable="true" 失效:
- 父级设置了
contenteditable="false"(继承规则是“就近 false 优先”) - CSS 中存在
user-select: none、pointer-events: none或display: contents - 元素在 Shadow DOM 内但未显式设置该属性
- 用了
<span></span>或其他内联元素——光标定位异常,建议只用于<div>、<code><section></section>等块级容器监听内容变更该用 input 还是 blur
用
input事件。它在每次按键、粘贴、格式操作后立即触发,比blur更及时,适合实时保存或校验。blur只在失焦时触发,用户可能编辑完直接关页,内容就丢了。但要注意:input不会捕获document.execCommand触发的格式变化(如加粗),因为 execCommand 不触发该事件——现代方案应避免依赖 execCommand,改用 Selection + Range 插入节点,这样才真正可控。示例写法:
const el = document.getElementById('editor'); el.addEventListener('input', () => { console.log('纯文本:', el.textContent); console.log('HTML(需清洗):', el.innerHTML); });粘贴富文本时 HTML 乱七八糟怎么处理
浏览器默认把 Word、微信、网页复制的内容原样塞进 DOM,带大量
style、嵌套<span></span>、data-属性甚至<script></script>标签残留。这不是 bug,是规范行为。正确做法是拦截
paste,禁用默认行为,再手动解析并清洗:- 调用
event.preventDefault() - 用
event.clipboardData.getData('text/html')拿原始 HTML 字符串(不是'text/plain',否则丢格式) - 用
DOMParser解析成文档片段,遍历节点做白名单过滤(只留<p></p>、<strong></strong>、<em></em>、<ul></ul>、<br>) - 删掉所有
style属性和class,避免污染样式体系 - 别用正则匹配 HTML 字符串——易漏、易错、不安全
如果业务只要纯文本,直接取
event.clipboardData.getData('text/plain'),跳过 HTML 解析环节更轻量。为什么不能直接用 innerHTML 存储和回显
因为
innerHTML是浏览器渲染后的结果,不是用户输入的语义表达。它会混入:- 浏览器自动插入的空
<div> 或 <code><p></p>(不同浏览器换行行为不一致) - 不可见的
<br>、零宽空格、 - 粘贴带进来的
data-spm、id、style等非用户意图属性 - XSS 风险:若未经滤直接插入服务端或再次 innerHTML 渲染,可能执行恶意脚本
落地存储前必须清洗:要么用
textContent强制转纯文本;要么用白名单 DOM 遍历器重建结构(例如把<b></b>统一转<strong></strong>,<font color></font>转<span style="color:"></span>);回显时也绝不能直接el.innerHTML = rawHtml,要走安全渲染流程。最常被忽略的一点:修改
innerHTML后光标必然丢失,且无法恢复到原位置——这是 contenteditable 的底层限制,任何基于 innerHTML 的“重载”都绕不开这个坑。 - 调用











