CKEditor5——模型理解(七:Selection)

昨天我们学习了Range的一些API使用,今天我们看看另一个重要的类Selection的API:
Selection的作用是记录鼠标在文档上的选择区域,如果是单个用户在编辑一份文档的时候,选择应该就是一个Range,如果是多个用户在编辑一份文档的时候,那么选择的区域就应该是多个range。因此,我大胆的猜测,Selection中应该有Range数组。我们来看看吧。
Selection属性
anchor : Position 选择的锚点
focus :Position 选择的聚焦点(光标的位置点)
isBackward :boolean 选择的方向(可能从锚点到聚焦点,也可能相反)
isCollapse : boolean
当选择只有一个Range,并且此Range是折叠的时候,那么选择就是折叠的。
rangeCount : number
选择的Range数目
让人觉得奇怪的是,Selection似乎没有Range数组,但是还是有一个Range数目的,猜测应该是一个私有属性。
Selection方法
Selection类有一个最重要的方法就是构造器:
constructor( [ selectable ] , [ placeOrOffset ] , [ options ] = { [ options.backward ] })
我们可以知道构造器有一个重要的参数就是Selectable接口,它的一些具体的类包括
Selection | DocumentSelection | Position | Range | Node | Iterable.<Range> | null
也就是说,只要是以上的类,都可以创建一个选择出来。我贴一下构造器的源码
constructor( selectable, placeOrOffset, options ) {
/**
* Specifies whether the last added range was added as a backward or forward range.
*
* @private
* @type {Boolean}
*/
this._lastRangeBackward = false;
/**
* Stores selection ranges.
*
* @protected
* @type {Array.<module:engine/model/range~Range>}
*/
this._ranges = [];
/**
* List of attributes set on current selection.
*
* @protected
* @type {Map.<String,*>}
*/
this._attrs = new Map();
if ( selectable ) {
this.setTo( selectable, placeOrOffset, options );
}
}
从构造器可以看出,Selection的功能主要有两方面,一个是可以存储属性,另一个是存储Range。它是通过将Selectable接口传到setTo()方法进行设置值。
setTo( selectable, placeOrOffset, options ) {
if ( selectable === null ) {
this._setRanges( [] );
} else if ( selectable instanceof Selection ) {
this._setRanges( selectable.getRanges(), selectable.isBackward );
} else if ( selectable && typeof selectable.getRanges == 'function' ) {
// We assume that the selectable is a DocumentSelection.
// It can't be imported here, because it would lead to circular imports.
this._setRanges( selectable.getRanges(), selectable.isBackward );
} else if ( selectable instanceof Range ) {
this._setRanges( [ selectable ], !!placeOrOffset && !!placeOrOffset.backward );
} else if ( selectable instanceof Position ) {
this._setRanges( [ new Range( selectable ) ] );
} else if ( selectable instanceof Node ) {
const backward = !!options && !!options.backward;
let range;
if ( placeOrOffset == 'in' ) {
range = Range._createIn( selectable );
} else if ( placeOrOffset == 'on' ) {
range = Range._createOn( selectable );
} else if ( placeOrOffset !== undefined ) {
range = new Range( Position._createAt( selectable, placeOrOffset ) );
} else {
throw new CKEditorError( 'model-selection-setto-required-second-parameter', [ this, selectable ] );
}
this._setRanges( [ range ], backward );
} else if ( isIterable( selectable ) ) {
// We assume that the selectable is an iterable of ranges.
this._setRanges( selectable, placeOrOffset && !!placeOrOffset.backward );
} else {
throw new CKEditorError( 'model-selection-setto-not-selectable', [this, selectable] );
}
}
在setTo()方法中,对Selectable接口的不同类型进行了分别处理,最终的结果就是设置Range。具体分析参考源代码就OK。
剩下的方法就是一些设置选择区域的属性,获取选择区域属性,移除属性,获取位置,获取Range以及一些判断犯法。
getSelectedBlocks() → Iterable.<Element>
获取选中的blocks,这样可以对选中的block进行迭代操作。
getSelectedElement()→Element
返回选中的元素。
基本的方法就是这些,重要的是理解为什么要这样设计?
版权声明:著作权归作者所有。