前言
之前看quill源码(一个富文本编辑器)的时候第一次接触Range与Selection对象,之前也写过一篇文章总结了这两个对象,不过感觉略水,正好现在在重构一个atwho的功能,特写几篇总结下。、
Selection
Selection是什么
selection是对当前激活选中区(即高亮文本)进行操作的对象。
Selection有什么用
我遇见的比较主要的就是获取选中区的Range
Selection例子
读者可以选中一些文字然后控制台输入下列代码
userSelection = window.getSelection();
userSelection.toString()
Range
Range是什么
所谓"Range",是指HTML文档中任意一段内容。一个Range的起始点和结束点位置任意,甚至起始点和结束点可以是一样的(也就是空Range)。最常见的Range是用户文本选择范围(user text selection)。当用户选择了页面上的某一段文字后,你就可以把这个选择转为Range。当然,你也可以直接用程序定义Range。
Range有什么用
试想一下,在富文本编辑器里面,有一些情况是加粗或者倾斜选中的内容,Range指HTML文档中任意一段内容。
在 HTML5 中,一个 Range 对象代表页面上的一段连续区域。通过 Range 对象,可以获取或修改页面上的任何区域。包含获取,修改,删除和替换等操作。
Range的例子
userSelection = window.getSelection();
userSelection.getRangeAt(0).toString()
Range与Selection
每一个selection对象都有一个或者多个Range对象,每一个range对象代表用户鼠标所选取范围内的一段连续区域,在firefox中,可以通过ctrl键可以选取多个连续的区域,因此在firefox中一个selection对象有多个range对象,在其他浏览器中,用户只能选取一段连续的区域,因此只有一个range对象。
所以你可以看到userSelection.getRangeAt(0)这个为什么会有个0
getRangeAt方法有一个参数index,代表该Range对象的序列号;我们可以通过Selection对象的rangeCount参数的值判断用户是否选取了内容;
属性与方法
由于这种属性和方法的东西太多了,罗列出来没什么用,我就说一些我用过的部分属性和方法。
Range对象的我用过的属性与方法(待补充)
setStart, setEnd等
setStart方法 用于将某个节点中的某处位置指定为Range对象所代表区域的起点位置,使用方法如下:
rangeObj.setStart(node,curIndex);
如上代码rangeObj代表一个Range对象,该setStart方法使用2个参数,第一个参数node代表一个节点,第二个参数是一个数字,当第一个参数node所代表的节点是一个内容为一段文字的文字节点时,该参数值用于指定将第几个文字的结束位置作为Range对象所代表的区域的起点位置;当第一个参数node所代表的节点中包括其他子节点时,该参数值用于将第几个子节点的结束位置指定为Range对象所代表的区域的起点位置;
Selection对象的我用过的属性与方法(待补充)
getRangeAt(index)
从当前selection对象中获得一个range对象。
index:参考rangeCount属性。
返回:根据下标index返回相应的range对象。
collapse(parentNode, offset)
将开始点和结束点合并到指定节点(parentNode)的相应(offset)位置。
实战
需求是获取光标之前的内容,这个在我实现atwho(@)和添加标签(#)的时候使用过。
代码
// 这个函数获取用户选择部分的Range对象
function getRange() {
const selection = window.getSelection()
if (selection && selection.rangeCount > 0) {
return selection.getRangeAt(0)
}
}
//这个获取光标前的Range
function getPrecedingRange() {
const r = getRange()
if (r) {
const range = r.cloneRange()
range.collapse(true)
// var el = closest(range.endContainer, d => d.contentEditable)
// range.setStart(el, 0)
range.setStart(range.endContainer, 0)
return range
}
}
// have fun
getPrecedingRange().toString()