最近想给自己的阅读器加上注释的功能,一番折腾之后发现 Rangy 这个好用的库。使用这个库可以便捷地实现获取和保存文本选取,以及对文本选取添加样式等功能。下面让我们来初步了解一下它的用法,更详细的内容可以查阅它的文档 Here 。
Selection 和 Range 对象
首先我们需要了解 Selection
和 Range
这两个和文本选取有关的对象。
Selection 对象表示用户选择的文本范围或插入符号的当前位置。它代表页面中的文本选区,可能横跨多个元素。文本选区由用户拖拽鼠标经过文字而产生。要获取用于检查或修改的 Selection 对象,请调用 window.getSelection()
。
1 | var sel = window.getSelection(); // 获取原生 Selection 对象 |
一个 Selection
对象可能包含多个 Range
对象,通常只包含一个。因为用户通常下只能选择一个范围,但是有的时候用户也有可能选择多个范围(例如当用户按下 Control 按键并框选多个区域时,Chrome 中禁止了这个操作,原译者注)。
注意:Rangy 库根据 DOM 规范自己实现了一个 Selection 对象和 Range 对象,下面方法的 API 中的 Selection 对象和 Range 对象都是指自己实现的对象。
1 | var sel = rangy.getSelection(); // 获取 **自定义** 的 Selection 对象 |
注意:在下面的 API 里,一个或多个函数或方法参数周围的方括号表示参数是可选的。
Serializer 模块
Serializer Module 可以对 Selection
对象和 Range
对象进行序列化和反序列化,有了它之后我们就可以实现注释的保存。
这个模块可以将鼠标选中的文本区域序列化为一个字符串进行存储,并在需要的时候反序列化以恢复成文本被鼠标选中的模样。
值得我们注意的是 Selection
对象的保存于 DOM 树密切相关,因此序列化和反序列化是比较“脆弱的”,对于动态页面可能不能做到正常的序列化和反序列化。另外,在一个浏览器上进行的序列化结果,可能不能在另一个浏览器被正常反序列化。
下面是两个常用的方法:
rangy.serializeSelection([Selection sel[, Boolean omitChecksum, [Element rootNode]])
- 不传入参数时,默认序列化当前页面的选中文本区域;传入参数
sel
时,会对传入的这个Selection
对象进行序列化。 - 参数
omitChecksum
表示是否省略校验和,这个在文档很大的时候建议把它设置为true
以加快速度。—— 注:“校验和”是为了弥补上面所说的“脆弱性”而进行的校验计算。 - 参数是
rootNode
,如字面意思“根节点”。Serializer Module 会处理“根节点”下的被选中的文本区域,将它们序列化。默认值是当前文档的根节点。 - 方法最后会返回一个字符串,代表选中部分
- 不传入参数时,默认序列化当前页面的选中文本区域;传入参数
rangy.deserializeSelection(String serializedSelection[, Element rootNode[, Window win]])
- 接收上一个方法返回的字符串作为参数,反序列化。
- 其余参数和上面方法参数类似
- 返回一个
Selection
对象
rangy.serializeRange(Range range[, Boolean omitChecksum, [Element rootNode]])
/rangy.deserializeRange(String serializedRange[, Element rootNode[, Document doc]])
与上面两个方法类似,是对Range
对象序列化和反序列化的版本。
对于上述方法的使用我们可以看看下面这个简单的例子:
1 |
|
1 | // 针对 iframe 的情况 |
Class Applier 模块
上面我们已经实现了获取文本选取和保存恢复文本选取的功能了,最后一步就是对选取应用样式了(比如说高亮选取部分)。
我们可以使用 Class Applier 模块,这个模块允许我们使用特定的标签包裹已经选取的文本,并且对此标签应用在 CSS 中已定义样式。
使用的方法如下:
rangy.createClassApplier(String theClass[, Object options[, Array tagNames]])
- 第一个参数
theClass
必须是在 CSS 中声明过的一个类,不可少。 - 第二个参数
options
是个配置对象,允许你对 class applier 的行为进行操作,主要包含下面的属性:elementTagName
:string,默认为"span"
,表示注释标签 。class applier 会使用<span>
标签包裹用户选中的区域,并对<span>
标签应用theClass
这个类,于是结果会变成这样:<span class="类名">用户选中的部分</span>
。elementProperties
:object。这个属性值是一个对象,对象的每个属性和值,都会变成注释标签的属性和值。如:elementProperties: { href: '#'}
,这个配置会让生成的注释标签变成这样<span href="#"></span>
。注意:这个属性很方便,甚至可以用elementProperties: { onclick: function () {} }
来挂载回调函数。onElementCreate
: 如名字所示,当注释标签被创建时调用的回调函数。回调函数接收两个参数:第一个是对创建标签的引用,第二个是当前的 class applier 对象。
- 函数返回一个
ClassApplier
对象,使用这个对象可以调用下面的方法来为选中区域添加或删除theClass
定义的样式。
- 第一个参数
applyToSelection([Window win])
- 为选中内容添加预设的样式,不提供参数时默认监听当前的窗口;也可以提供一个
Window
对象(比如操作iframe
时可以传入iframe.contentWindow
,下同)
- 为选中内容添加预设的样式,不提供参数时默认监听当前的窗口;也可以提供一个
undoToSelection([Window win])
- 消除 class applier 为选中内容添加的样式。
isAppliedToSelection([Window win])
- 判断选中部分 都 被 class applier 添加了预设的样式
toggleSelection([Window win])
- 有则添加,无则删除预设的样式
applyToRange(Range range)
/undoToRange(Range range)
/isAppliedToRange(Range range)
/isAppliedToRange(Range range)
/toggleRange(Range range)
- 作用和上面对应方法类似
对于上述方法的使用我们可以看看下面这个简单的例子:
1 |
|