选区

lexical
Created 2/9/2023
Updated 6/17/2023

选区

参考

选区 selection 是指编辑器内容中被选中的区域,有 4 种类型的选区:

  • RangeSelection 范围选区
  • NodeSelection 节点选区
  • GridSelection 网格选区
  • null 表示选区为空
提示

选区是 Editor State 编辑器状态里的一部分,也就是说每次编辑器更新时选区也会同步更新,这样就确保了选区在更新前后的(相对于 node tree 节点树)一致性。

Lexical 提供了 helper function 方法 $getSelection() 获取编辑器当前的选区

注意

$getSelection() 该方法只能在 editor.update(callback) editorState.read(callback)editor.registerCommand(callback) 的回调函数中使用

ts
import { $getSelection } from 'lexical';

editor.update(() => {
  const selection = $getSelection();
});

Lexical 还提供了一个内置指令 SELECTION_CHANGE_COMMAND 当编辑器的选区改变时就会自动分发该指令

提示

相当于 DOM 原生的 selectionchange 事件

ts
// SELECTION_CHANGE_COMMAND 是内置指令
// 当编辑器的选区改变时就会分发
// 类似于 document.onselectionchange() 的作用
import { $getSelection, SELECTION_CHANGE_COMMAND } from 'lexical';

// 为 SELECTION_CHANGE_COMMAND 指令注册响应函数
editor.registerCommand(SELECTION_CHANGE_COMMAND, () => {
  const selection = $getSelection();
  console.log(selection);
}, 0);

RangeSelection

范围选区 RangeSelection 是最常见的一种选区类型

提示

它参考自浏览器的 DOM 原生的 SelectionRange API,并进行了规范化。

所以该选区实例对象中不少属性和原生选区的概念相似。

关于原生 Selection 和 Range API 的详细介绍可以参考 MDN 相关页面或这个教程

这种类型的选区对象有三个属性:

  • anchor 属性:一个对象,表示范围选区的一侧端点(锚定「不动」的一端,即框选开始时光标的位置
  • focus 属性:一个对象,表示范围选区的另一侧端点(焦点「延伸」的一端,即框选结束时光标的位置
    说明

    因为用户可以从左向右框选,也可以从右向左框选,所以 anchor 所对应的光标位置可能在 focus 所对应的光标位置的右侧


    以上两个属性都是一个对象,以表示编辑器中的一个位置,其中主要包含的属性如下:
    • key 属性:一个数字,是该选区端点所在的节点的标识符 NodeKey
    • offset 属性:一个数字,表示选区的端点位于节点的何处(偏移量)。如果是文本节点,则以字符为单位来计算;如果是元素节点,则以子节点为单位来计算。序号从 0 开始算起,如果光标在节点的开头,则 offset0
    • type 属性:elementtext,表示该选区端点所在的节点类型
  • format 属性:以(经过位运算的)数字表示当前被选中的所含有的文本格式

以下是一些范围选区的例子

选中一个文本节点
选中一个文本节点

选区在一个文本节点内
选区在一个文本节点内

选区横跨几个文本节点
选区横跨几个文本节点

NodeSelection

节点选区 NodeSelection 是指选择多个节点,如三个图片被同时选中

可以通过方法 nodeSelection.getNodes() 返回一个数组,其中各元素就是选中的节点

GridSelection

网格选区 GridSelection 是指对网格布局的内容进行框选,例如表格。

这种类型的选区对象主要有以下三个属性:

  • gridKey 属性:表示选区所在的父节点(即网格布局的容器),用一个数字表示节点
  • anchor 属性:一个数字(表示网格的某个单元),表示网格选区的一端的单元格(锚定「不动」的一端)
  • focus 属性:一个数字(表示网格的某个单元),表示网格选区的另一端的单元格(焦点「延伸」的一端)

null

当编辑器的内容没有被选中时,或编辑器(可编辑元素)失去焦点时,或在选中了编辑器里(不可编辑的)组件的内容时,则该编辑器的选区就是 null

编程式创建选区

可以通过一些 helper function 方法来创建选区(即以编程式的方式来创建选区,而不是用户手动框选创建选区)

ts
import { $setSelection, $createRangeSelection, $getNodeByKey, $createNodeSelection } from 'lexical';

// 以编程式的方式创建选区
// 其流程和使用浏览器原生的 Selection 和 Range API 创建选区的流程类似
editor.update(() => {
  /**
   * 范围选区
   */
  // 初始化/创建一个(空的)范围选区对象
  const rangeSelection = $createRangeSelection();
  // 将该选区应用到编辑器中
  // 不过实际上需要等到这一次更新 commit 到**页面**上时,才会真的框选住相应的内容
  $setSelection(rangeSelection);

  // 还可以通过节点的相关方法来*间接*创建范围选区
  const someNode = $getNodeByKey(someKey);

  // On element nodes, this will create a RangeSelection with type "element",
  // referencing an offset relating to the child within the element.
  // On text nodes, this will create a RangeSelection with type "text",
  // referencing the text character offset.
  someNode.select();
  someNode.selectPrevious();
  someNode.selectNext();

  // On element nodes, you can use these.
  someNode.selectStart();
  someNode.selectEnd();

  /**
   * 节点选区
   */
  const nodeSelection = $createNodeSelection();
  // Add a node key to the selection.
  nodeSelection.add(someKey);
  $setSelection(nodeSelection);

  /**
   * 清除选区
   */
  // 将选区设置为 null
  $setSelection(null);
});
注意

这些 helper function 方法只能在 editor.update(callback)editor.registerCommand(callback) 的回调函数中使用


Copyright © 2025 Ben

Theme BlogiNote

Icons from Icônes