JavaScript 里怎样用 Selection 和 Range API 精确操作网页文本选区

Cora
Cora
发布时间:2026-08-25 | 123人浏览过
原创

前言

在网页里做高亮、批注或富文本编辑时,最容易混淆的就是 Selection 和 Range:一个表示用户当前选中了什么,另一个决定脚本实际操作哪一段。下面按读取选区、创建范围、让高亮生效、再到删除与包裹内容的顺序拆开讲,帮助你判断这两个 API 分别该在什么场景下使用。

在网页里做文本选中、批注、高亮或轻量编辑时,很多问题都卡在“看得见的选区”和“代码里的操作范围”不是一回事。本文把 window.getSelection() 与 document.createRange() 分开讲清,再串起来说明它们如何配合,让你能判断当前选区、主动指定范围,并把删除、插入、包裹等操作准确落到目标文本上。

先分清:Selection 和 Range 各管什么

window.getSelection() 获取的是浏览器当前维护的全局选区,重点在“用户现在选了哪里”。而 document.createRange() 创建的是一个可编程范围,重点在“代码准备操作哪一段”。

实际开发里,这两个 API 往往需要一起用:

  • 先通过 window.getSelection() 读取用户当前选中的内容
  • 再通过 document.createRange() 创建或调整精确边界
  • 最后把 Range 交回 Selection,才能在页面上真正显示成选中状态

换句话说,Selection 更像页面当前的“选中结果”,Range 则是你可以拿来读、写、高亮、格式化文本的“操作坐标”。

如何获取并判断当前选区

当用户在页面中拖选文本后,浏览器会自动维护一个 Selection 对象。最常见的入口就是:

const sel = window.getSelection()

拿到它之后,可以先做几个基础判断。

读取选中的文本

如果只关心用户选中了什么内容,最直接的方法是:

sel.toString()

这个调用返回的是选中的纯文本,适合做复制、提示或快速判断。

判断有没有真正选中内容

有时页面里看起来有光标,但其实并没有有效选区。这时可以检查:

用便签卡片展示 Selection 与 Range 的职责分工和它们在文本选区流程中的关系
Selection 与 Range 怎么配合Selection 代表当前可见选区,Range 负责精确边界,真正显示高亮时需要两者配合。
  • sel.rangeCount === 0:表示没有任何 Range
  • sel.isCollapsed 为 true:表示起点和终点重合,当前只是光标状态

这两个条件很常用于编辑器、评论框和批注工具里,避免在空选区上误执行格式化或删除动作。

理解选区的起点和终点

Selection 还保存了选区两端的信息:

用纸面步骤图展示 Range 在单节点和跨节点中的定位方式,以及可执行的典型操作
Range 的定位方式与常见操作Range 的关键价值在于边界精确:既能按偏移定位,也能跨节点圈定内容。
  • 起点:sel.anchorNode / anchorOffset
  • 终点:sel.focusNode / focusOffset

这里要注意,起点和终点可能位于不同节点中,尤其是在跨标签选中文本时更常见。也正因为如此,直接依赖字符串处理往往不够稳,通常还是要交给 Range 按节点边界来做精确操作。

如何创建并精确定位一个 Range

如果说 Selection 反映的是当前状态,那么 Range 更像一把可编程的“文本尺子”。它不依赖用户手动拖选,完全可以由脚本主动创建:

const range = document.createRange()

在单个文本节点里设置范围

当目标内容位于纯文本节点内部时,可以直接通过偏移量设定起止位置。比如:

range.setStart(textNode, 3)
range.setEnd(textNode, 8)

如果节点内容是 Hello world,那么这段范围圈出的就是 lo wor。这种写法适合做字词级高亮、局部替换或精确插入。

在多个子节点之间设置范围

如果目标跨越多个子节点,就可以把元素节点本身作为参考。比如结构是:

<p>Hi <em>there</em>.</p>

这时可以这样设定:

range.setStart(p, 0)
range.setEnd(p, 2)

range.setStart(p, 0) 表示从 p 的第 0 个子节点开始,range.setEnd(p, 2) 表示到第 2 个子节点之前结束。它适合处理跨标签选区,不需要把内容先拍平成一段字符串再回填到 DOM。

几个常用快捷方法

  • range.selectNodeContents(el):选中整个元素内部内容
  • range.collapse(false):把范围折叠到末尾,变成一个光标位置

前者常用于“一键选中整段内容”,后者常用于把光标移动到编辑器末尾或指定位置。

为什么创建了 Range 还要交给 Selection

很多人第一次接触这组 API 时会疑惑:Range 明明已经定义好了边界,为什么页面上看不到蓝色高亮?原因是 Range 只是代码中的范围对象,要让浏览器把它显示成当前选区,还需要交给 Selection。

典型流程如下:

sel.removeAllRanges()
sel.addRange(range)
  • sel.removeAllRanges() 先清空现有选区
  • sel.addRange(range) 再把刚创建的 Range 加进去

这样浏览器才会把这段范围显示为真正的选中状态。

把光标移动到指定位置

如果你的目标不是高亮文本,而是把光标挪到某个位置,比如编辑器末尾,也可以先创建 Range,再折叠后加入 Selection。关键点不在“选中多长”,而在“折叠后把当前位置交给浏览器”。

关于多选区的限制

理论上 Selection 可以包含多个 Range,但在多数浏览器里,可见的多 Range 高亮支持有限,尤其不适合当成稳定能力来依赖。实际项目中,通常还是按“一次只处理一个可见选区”来设计更稳妥。

拿到 Range 后,能直接做哪些操作

真正进入富文本编辑、批注或内容增强场景后,Range 的价值就体现出来了。只要边界设得准,后续操作就能尽量避免误伤周边 DOM 结构。

复制选中片段

range.cloneContents()

这个方法会复制选中部分的 DOM 片段,而且会保留其中的标签结构,不只是纯文本。适合做富文本复制、片段预览或二次加工。

删除选中内容

range.deleteContents()

执行后,选中的内容会被删除,DOM 会自动重排。相比手工查找节点再逐个删,这种基于范围边界的方式更适合编辑型交互。

包裹选中的内容

range.surroundContents(document.createElement('strong'))

这会把选中文本包进一个 <strong> 元素中,是实现加粗、强调或特定标注的常见手法。

在选区插入新节点

range.insertNode(node)

如果你需要插入链接、图标或其他 DOM 节点,可以直接使用这个方法。只要 Range 的起点和终点定义正确,插入动作就会准确落在目标位置,而不会波及周围无关内容。

总的来说,Selection 负责承接用户当前的选中状态,Range 负责给脚本提供精确边界。把这层关系理顺后,网页里的文本读取、高亮、格式化和编辑动作就会清晰很多,也更容易做出稳定的交互实现。

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表