敲碎时间,铸造不朽的个人专栏
上一篇

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

返回选中的元素。

基本的方法就是这些,重要的是理解为什么要这样设计?

版权声明:著作权归作者所有。

X

欢迎加群学习交流

联系我们