selection是精准控制文本操作的唯一入口,所有用户划选、脚本定位、高亮均依赖它;必须检查rangecount>0且注意上下文限制,跨iframe需用contentwindow.getselection()。

HTML选区(Selection)本身不提升文本操作,但它是精准控制文本操作的唯一入口
浏览器里所有用户手动划选、脚本触发的光标定位、高亮范围,都必须通过 Selection 对象读取或修改。它不是“加速器”,而是开关——没它,document.execCommand(已废弃)、Range 操作、输入框外的富文本编辑全都会失效。
常见错误现象:getSelection().toString() 返回空字符串,但页面明明有选中文字;或调用 selectAllChildren() 后视觉无变化——本质是忽略了当前 Selection 可能为空、或处于不可编辑上下文(比如 contenteditable="false" 的容器内)。
- 必须先检查
getSelection().rangeCount > 0,再取getSelection().getRangeAt(0) -
Selection是全局单例,跨 iframe 需用iframe.contentWindow.getSelection() - 在
input或textarea中,优先用.selectionStart/.selectionEnd,它们比Selection更稳定、支持设置
用 Range 替代直接操作 innerHTML 是避免 DOM 错乱的关键
想把用户选中的“abc”替换成带样式的 <span class="highlight">abc</span>?别碰 element.innerHTML = ...。它会销毁原 DOM 节点,导致 Selection 瞬间丢失、事件监听器脱钩、焦点消失。
正确路径永远是:从 Selection 获取 Range → 拆分文本节点(range.splitText())→ 插入新节点(range.insertNode())→ 恢复焦点(range.selectNodeContents() 后 sel.removeAllRanges(); sel.addRange(range))。
-
Range是可编辑的底层单元,Selection只是它的“视图” -
range.cloneContents()和range.extractContents()行为差异极大:前者只复制,后者会移除原文本 - 在 Shadow DOM 中,
Range必须在对应shadowRoot内创建,跨边界会报InvalidStateError
移动端 Safari 的 Selection API 有硬性限制
iOS 上,用户长按选中文本后,getSelection() 在非 contenteditable 元素中返回空;即使加了 -webkit-user-select: text,脚本也无法通过 selectNode() 主动触发选区——这是 Safari 的安全策略,不是 bug。
可行方案只有两个:把目标区域设为 contenteditable="true"(哪怕只是临时启用),或改用 textarea + setSelectionRange() 模拟。
-
document.designMode = "on"在 iOS 上完全无效,别试 -
focus()不保证唤起键盘,需配合input元素的type="text"或显式click() - Safari 16.4+ 开始支持
Selection.modify(),但仅限键盘事件触发,脚本调用被静默忽略
不要依赖 document.execCommand,但得知道它为什么崩了
document.execCommand("bold") 看似方便,但它强制依赖当前 Selection 状态、会污染 HTML 结构(插入冗余 <b></b> 或 <font></font>)、且在 Chrome 98+ 和 Firefox 与 Safari 中已被标记为废弃。现在调用它不会报错,但返回 false,且行为不可预测。
替代方案不是重写整个富文本编辑器,而是聚焦具体需求:加粗就用 Range 包裹并插入 <strong></strong>;插入链接就创建 Range + document.createElement("a") + range.surroundContents()(注意该方法要求 Range 必须完全位于单个文本节点内)。
-
surroundContents()容易抛InvalidStateError:Range 跨了两个兄弟节点、或包含元素节点时必失败 - 想兼容旧项目?可以用
try/catch包裹execCommand,失败时降级到Range方案 - 现代框架(如 Slate、Tiptap)内部早已弃用
execCommand,它们的“格式化”本质都是Range+DOM mutation
真正难的从来不是“怎么选中”,而是选中之后如何让 DOM 变更不破坏用户当前的光标位置、滚动偏移、输入法状态——这些细节藏在 Range 的边界点(startContainer/endOffset)和 Selection 的 collapse() 调用时机里,漏掉一个,粘贴或回车就出问题。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











