JavaScript Selection.setBaseAndExtent 怎样设置带方向的文本选区
来源:17golang原创
时间:2026-10-27 14:19:30 206浏览 收藏
Selection.setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset) 用两组边界点设置选区。第一组是锚点,第二组是焦点;焦点在文档顺序中位于锚点之前时,得到反向选区。要保留方向,就不能先把这两组参数按大小重新排序。
文档入口:https://developer.mozilla.org/en-US/docs/Web/API/Selection/setBaseAndExtent。下面用同一文本节点说明 API 语义,再讨论恢复选区的边界。这是既有 Web API 的用法解析,不是新的语言提案;示例按文档编写,未在本文环境执行。
用最小例子建立反向选区
在已有页面上添加一个文本容器和按钮。通过按钮触发设置,避免页面初始化时无条件抢占用户选区:
red green blue
// 在上方HTML之后运行,保证元素已存在
const text = document.getElementById("sample").firstChild;
const button = document.getElementById("select-word");
button.addEventListener("click", () => {
const selection = window.getSelection();
if (!selection || typeof selection.setBaseAndExtent !== "function") {
// 由产品提供不支持方向恢复时的提示
return;
}
// green占据[4, 9),锚点在词尾,焦点在词首
selection.setBaseAndExtent(text, 9, text, 4);
});
这里的字符串位置可由原文直接推导:red 后有一个空格,green 从偏移4开始、在偏移9之前结束。交换为 (text, 4, text, 9) 会覆盖相同文字,但锚点和焦点角色不同。不要把“选中文字相同”当成“选区状态完全相同”。
分清锚点焦点与范围起止
anchor 表达选择的基点,focus 表达另一端。Range 的 start 与 end 则按文档顺序组织,反向选择并不意味着 start 大于 end。因此,只保存 getRangeAt(0) 的起止位置,并不能完整保存锚点与焦点的角色。

方向描述的是文档边界顺序,不等同于屏幕上的左右方向。包含双向文字或复杂布局时,更不能仅凭视觉上的“向左拖动”推断 DOM 端点。本文例子限于普通文档中的单一文本节点。
按节点类型计算偏移
Text 节点的偏移按 UTF-16 代码单元计数;Element 节点的偏移按子节点位置计数。空白文本节点也属于子节点,所以 children 的长度不能直接当作 childNodes 的长度使用。跨元素选择时,每一端都必须有自己的节点和该节点内偏移。
// 示例只接受明确的Text节点,避免把元素偏移误当字符偏移
function selectTextPart(textNode, anchor, focus) {
if (!textNode || textNode.nodeType !== Node.TEXT_NODE) {
throw new TypeError("需要文本节点");
}
for (const offset of [anchor, focus]) {
// 先限制输入为非负整数,再检查节点内上界
if (!Number.isInteger(offset) || offset textNode.length) {
throw new RangeError("偏移超出文本节点范围");
}
}
if (!textNode.isConnected || textNode.ownerDocument !== document) {
throw new Error("节点必须位于当前文档");
}
const selection = window.getSelection();
if (!selection || typeof selection.setBaseAndExtent !== "function") {
return false; // 将不支持情况交给调用方处理
}
selection.setBaseAndExtent(textNode, anchor, textNode, focus);
return true;
}
这段封装有意只处理当前文档内的文本节点,不是可覆盖 Shadow DOM 或所有编辑器模型的通用库。不要把后端的字节偏移直接传进来;emoji 也可能占据两个代码单元,组合字符还可能由多个代码点构成。面向用户字符的索引需要先映射到文本节点偏移。

保存方向时保留两端角色
旧写法常把范围起止当成选区全部状态。对于需要恢复用户选择方向的编辑工具,可以先保存 anchorNode、anchorOffset、focusNode、focusOffset,再在节点仍然有效时调用 setBaseAndExtent。调用会替换当前选择范围,无须为了这一次设置先 removeAllRanges。
// 只适用于DOM节点身份与文本内容都未改变的短期保存
function captureSelection() {
const s = window.getSelection();
if (!s || s.rangeCount === 0 || !s.anchorNode || !s.focusNode) return null;
return {
anchorNode: s.anchorNode, anchorOffset: s.anchorOffset,
focusNode: s.focusNode, focusOffset: s.focusOffset
};
}
// 在受控DOM更新后,应先重建有效端点,不能盲用旧节点引用
function restoreSelection(saved) {
const s = window.getSelection();
if (!saved || !s || typeof s.setBaseAndExtent !== "function") return false;
if (!saved.anchorNode.isConnected || !saved.focusNode.isConnected) return false;
try {
s.setBaseAndExtent(
saved.anchorNode, saved.anchorOffset,
saved.focusNode, saved.focusOffset
);
return s.anchorNode === saved.anchorNode &&
s.anchorOffset === saved.anchorOffset &&
s.focusNode === saved.focusNode && s.focusOffset === saved.focusOffset;
} catch (error) {
if (error.name === "IndexSizeError") return false; // 文本缩短导致偏移失效
throw error;
}
}
框架重渲染可能替换节点;即便节点没换,文本内容变化也会改变原偏移代表的位置。长期书签应使用编辑器自己的稳定位置模型,并映射回当前 DOM;单纯 JSON 序列化节点引用不能完成这件事。
处理兼容与常见边界
| 目标 | 合适方式 | 容易忽略的点 |
|---|---|---|
| 普通文档中恢复两端角色 | setBaseAndExtent | 不要先排序anchor和focus |
| 只处理文档中的覆盖范围 | Range边界 | 范围起止不完整表达选择方向 |
| 文本输入框内选区 | setSelectionRange | 使用控件value的索引 |
input 与 textarea 应使用各自的文本选择接口。例如 text 类型输入框可以调用 setSelectionRange(start, end, "backward"),并按交互需要让控件获得焦点。不要把输入框的 value 当成可直接选择的子 Text 节点。
偏移超出节点长度会自动截断吗?
不要依赖截断。setBaseAndExtent 的越界偏移会触发 IndexSizeError,封装应提前明确输入范围。
两端相同会报错吗?
同一边界点表示折叠选区,即插入位置;它不包含一段被选中的文字。
接口存在就能覆盖所有编辑器吗?
不能。普通文档、表单控件、嵌套文档和 Shadow DOM 的处理边界不同,特性检测只说明方法存在,不能替代应用自己的节点映射。
-
502 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
488 收藏
-
266 收藏
-
486 收藏
-
262 收藏
-
209 收藏
-
300 收藏
-
406 收藏
-
214 收藏
-
138 收藏
-
483 收藏
-
260 收藏
-
118 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习