deletecontents() 清空选区内容但不删除容器元素本身;需先克隆range或detach以避免invalidstateerror;区别于extractcontents()(返回documentfragment并删除)和clonecontents()(仅复制)。

deleteContents() 会清空选区,但不移除容器元素本身
deleteContents() 的作用是「删除选区内的文本节点和元素节点的内容」,但它不会把包裹选区的父元素一并删掉。比如你用鼠标拖选了 <div>hello <strong>world</strong>
</div> 中的 hello <strong>world</strong>,调用后 <div> 还在,只是变为空标签:<code><div></div>。这点容易误以为“整个 div 消失了”,其实只是内容被清空。
常见错误现象:执行后页面视觉上没变化,或只删了一半内容——往往是因为选区跨了多个块级容器(比如从 <p></p> 开始、跨到 <ul></ul>),而 deleteContents() 只处理选区内直接包含的节点,不递归清理空父节点。
- 必须先确保有有效选区:
getSelection()返回非 null,且rangeCount > 0 - 选区需由用户交互(如鼠标拖选)或脚本显式设置;纯空字符串选区或折叠选区调用后无效果
- 若要连同空父元素一起移除,得额外遍历并判断:
parentNode.childNodes.length === 0且不是body或不可删容器
调用前必须 detach 范围,否则可能报 DOMException
Range 对象一旦被添加进选区(例如通过 selection.addRange(range)),再直接调用 deleteContents() 会触发 "InvalidStateError" 错误。这是因为浏览器禁止对已绑定到 Selection 的 Range 执行修改操作。
正确做法是:先用 range.cloneRange() 复制一份独立副本,再对副本调用 deleteContents();或者更稳妥地,在操作前调用 range.detach()(尽管现代浏览器大多已不强制要求,但 Safari 15.4–16.3 仍有此限制)。
- 安全写法:
const r = range.cloneRange(); r.deleteContents(); - 不要写:
selection.getRangeAt(0).deleteContents()—— 这大概率失败 - 注意:
detach()后该 Range 不再关联文档,也不能再用于selectNode()等操作
与 extractContents() 和 cloneContents() 的关键区别
这三个方法都作用于同一个 Range,但行为完全不同:deleteContents() 是「删掉且不保留」,extractContents() 是「删掉并返回 DocumentFragment」,cloneContents() 是「只复制,不删」。选错就等于白操作。
典型误用场景:想剪切选区内容到剪贴板,却用了 deleteContents(),结果内容丢了还没存下来。这时候应该用 extractContents(),它返回的 DocumentFragment 可以塞进 ClipboardEvent.clipboardData.setData() 或插入到其他位置。
-
deleteContents()→ 无返回值,原地清除 -
extractContents()→ 返回DocumentFragment,原位置清空 -
cloneContents()→ 返回DocumentFragment,原位置不变 - 三者都不影响 Range 自身的起止位置,后续仍可调用
surroundContents()(但要注意被包围节点不能是空或含块级元素)
在 contenteditable 区域中 deleteContents() 的兼容性陷阱
在 contenteditable="true" 元素里,浏览器默认会监听用户输入并自动维护选区,此时手动调用 deleteContents() 容易和原生编辑逻辑冲突。例如 Chrome 中连续快速删除可能导致光标跳到意外位置,Firefox 下某些嵌套 span 可能残留零宽空格(\u200B)。
根本原因在于:原生编辑器会在每次 DOM 变更后重置选区,而 deleteContents() 触发的是底层 DOM 修改,不一定同步触发编辑器的状态更新。最稳妥的做法是避免直接操作 Range,改用 document.execCommand('delete', false, '')(虽已废弃但兼容性更好),或在现代项目中用 InputEvent + getComposedText() 模拟。
- 如果必须用
deleteContents(),请在setTimeout(() => {}, 0)中执行,让浏览器先完成一次渲染周期 - 删除后建议手动恢复光标位置:
selection.removeAllRanges(); selection.addRange(newRange); - 移动端 WebView(尤其 iOS)对 Range 操作支持不稳定,
deleteContents()可能完全静默失败
deleteContents() 表现高度依赖上下文。动手前务必用 range.toString() 和 range.getBoundingClientRect() 验证选区是否真如你所见。











