如何从Slate.js编辑器中获取不受文档结构影响的绝对文本选区
解决Slate.js中不受文档结构影响的选区位置计算问题
我用React和TypeScript实现基于Slate.js的协作文本编辑器,需要获取用户修改样式的精确文本选区,自定义的选区类型定义如下:
type Cursor = { line: number; column: number; }; type Selection = { start: Cursor; end: Cursor; };
当前实现受Slate文档树结构(文本样式)影响,无法获取不受结构影响的选区,且列数计算错误。现有代码:
function getSelection(editor: Editor): Selection { const { selection } = editor; if (!selection) { return { start: { line: 0, column: 0 }, end: { line: 0, column: 0 }, }; } const [start, end] = Range.edges(selection); return { start: pointToCursor(start), end: pointToCursor(end), }; } function pointToCursor(point: Point): Cursor { return { line: point.path[0], column: point.offset, // 错误:offset是当前文本节点内的偏移,不是整行累计偏移 }; }
问题示例
- 当整行是单一文本节点
abcdef,选中def时,返回列数[0, 3],不符合预期的[3, 6] - 当
abc和def分属不同样式的文本节点时,返回列数[3, 6],符合预期
解决方案
核心是计算整行的累计文本长度,而非仅当前文本节点内的偏移。修改pointToCursor函数,遍历当前行的所有前置文本节点,累加长度后加上当前节点的offset,得到全局列数:
import { Editor, Point, Node, Text } from 'slate'; function pointToCursor(editor: Editor, point: Point): Cursor { const lineIndex = point.path[0]; // 获取当前行节点 const lineNode = Node.get(editor, [lineIndex]) as any; let totalOffset = 0; const currentTextNodeIndex = point.path[1]; // 遍历当前行的所有子节点,累加前置文本节点的长度 for (let i = 0; i < currentTextNodeIndex; i++) { const child = lineNode.children[i]; if (Text.isText(child)) { totalOffset += child.text.length; } } // 加上当前文本节点内的偏移,得到整行的列数 return { line: lineIndex, column: totalOffset + point.offset, }; } // 同时更新getSelection函数,传入editor到pointToCursor function getSelection(editor: Editor): Selection { const { selection } = editor; if (!selection) { return { start: { line: 0, column: 0 }, end: { line: 0, column: 0 }, }; } const [start, end] = Range.edges(selection); return { start: pointToCursor(editor, start), end: pointToCursor(editor, end), }; }
说明
- 通过
Node.get获取当前行节点,确保拿到完整的行内容 - 遍历当前文本节点之前的所有子节点,累加文本长度(只处理Text类型节点)
- 将累计长度与当前Point的offset相加,得到不受文档结构影响的全局列数
- 无论一行是单一文本节点还是多个样式节点,都能返回正确的整行列数
内容的提问来源于stack exchange,提问作者r1c4rdco5t4
相关产品推荐
相关产品推荐

