Range对象详解
- 什么是
Range
对象?
Range表示HTML文档的一部分内容,它可以在任何点开始和结束,最常见的Range就是用户选择的一段文本。通过Range对象,你可以找到开始点和结束点,你可以复制或者删除它,或者替换成另一段文本,甚至是一段HTML代码。
获取Range
对象有三种方法:
document.createRange 方法
1var range = document.createRange();Selection
对象的getRangeAt
方法12var selection = window.getSelection() ; // 获取用户选中的内容Selection对象var range = selection.getRangeAt( 0 ) ; // 从Selection对象获取Range对象Range
的构造函数来创建一个Range
对象1var range = new Range() ;
下面介绍一下Range
对象(实例)的属性:
Range.collapsed
只读,返回一个判断当前Range
对象的起始位置和终止位置是否相同的布尔值。例1-1:
123var selection = window.getSelection() ; // Selection对象var range = selection.getRangeAt( 0 ) ; // W3C Range对象console.log( range.collapsed ) ;当选中’年底’二字的时候,起始位置和终止位置不同,返回false;当光标在’年底’之后,起始位置和终止位置相同,collapsed返回true。
commonAncestorContainer
只读,返回包含startContainer
和endContainer
的最深的节点一般为文本节点,比如选中’年底’,返回的是包含’年底’的那个文本节点。
注意⚠️:返回的文本节点,是包含年底二字的文本节点,’每到年底…’整段都是那个文本节点。如果
Range
对象跨标签,<p>每到年底,很多新闻</p><span>比如洞察宇宙的</span>
当选中很多新闻</p><span>比如
这样子的文本,返回的最深节点为包含p
标签和span
标签的共同父标签。startContainer
/endContainer
只读,返回Range
对象开始和终止的节点对象。一般选中文字,返回的都是文本节点
startOffset
/endOffset
只读,表示Range起始位置和终止位置的数字例1-2:
123var selection = window.getSelection() ; // Selection对象var range = selection.getRangeAt( 0 ) ; // W3C Range对象console.log( range.startOffset , range.endOffset ) 当
Range.collapsed
为true是,startOffset和endOffset值相等。
下面介绍一下Range
对象拥有的方法:
1、Range.setStart()
/Range.setEnd()
设置Range
的起点和终点,这两个方法都接受两个参数Node和offset;如果起始节点是Text,Comment, or CDATASection之一,那么offset指的是从起始节点算起的字符的偏移量,对于其他Node类型节点,offset是指从起始节点开始算起子节点的偏移量[^2]。
例1-3:
|
|
设置startOffset为1,从’每’第一个字符后开始,设置endOffset为,起始之后的4个字符,逗号之后。最后通过Selection
对象的removeAllRanges()
和addRange()
方法,建立拖蓝^1的文本选区。Selection
对象的API可以看MDN的文档,见下面的参考资料超链接。
2、Range.setStartBefore()
/Range.setStartAfter()
/Range.setEndBefore()
/Range.setEndAfter()
以其他节点之前,之后为基准,设置Range
对象的起止点。这四个方法共同接受一个参数,referenceNode。
比如,你想要选中<p>每到年底,很多新闻机构都会评选年度新闻。小编注意到,习主席今年贺词提到的具体成就格外关注科技和创新。</p>
整个p
标签的文本,可以:
例1-4
|
|
3、Range.selectNode()
设置Range
对象包含指定的Node
节点和它的内容,参数为需要选中的Node,referenceNode;执行之后,Range
对象的startContainer和endContainer为传入的referenceNode的父元素。
4、Range.selectNodeContents()
设置Range
对象包含指定Node
节点的内容,例1-4的代码可改写成如下:
例1-5:
|
|
细心的你可能发现第三行代码,最后的获取的节点不一样了,有firstChild
的获取的是text节点,没有的,获取的是element节点。setStartBefore
传入的参数若是一个element节点,则Range
对象会包含这个element节点,因为选区的内容是从element节点之前开始的。倘如传入的是element节点下面的text节点,则Range
对象不会包含这个element节点,单单只包含text节点的内容。
调用了range.selectNodeContents
方法之后:
The parent
Node
of the start and end of theRange
will be the reference node. ThestartOffset
is 0, and theendOffset
is the number of childNodes
or number of characters contained in the reference node.
翻译一下,意思大概:
Range
对象的开始节点和结束节点的父节点为传入的reference node,起始偏移量为0,结束偏移量为子节点的个数[^2]或者reference node节点包含的文本字符数。
我测试了一下,当执行上面的代码之后:console.log( range.startContainer === pTextNode , range.endContainer === pTextNode )
打印了一下,true true
,结果两个都是为true。正确意思应该是startContainer和endContainer都应该是reference node,而不是它们的父节点是reference node。这个和Range.selectNode
的方法实现有点不一样,Range.selectNode
执行完之后,startContainer和endContainer代表的是reference node的父元素。
4、Range.collpase()
移动Range
对象的开始或者结束节点,到另外一个边界点。即,折叠开始节点到结束节点,或者折叠结束节点到开始节点,让开始和结束节点重合。这个方法接受一个布尔值的参数,如果为true,则折叠结束节点到开始节点,false,折叠开始节点到结束节点。折叠之后的Range
对象是空的,没有内容,在dom树中指定了一个单一的入口节点。判断一个Range
对象是否已经被折叠过了,可以查看Range.collapsed
属性。
例1-6:
|
|
光标选中了pTextNode的句号之后的最后一部分。
以上为定位方法,下面介绍编辑方法:
5、Range.cloneContents()
返回DocumentFragment类型的Range
中节点的文档片段。事件监听器不会被复制,html的属性中绑定的事件会被复制,id也会被复制,所以处理的时候要小心。
例1-7:
|
|
被复制出来的节点内容是:<p>每到年底,很多新闻机构都会评选年度新闻。小编注意到,习主席今年贺词提到的具体成就格外关注科技和创新。</p>
包含标签。
6、Range.deleteContents()
从文档中移除Range
选中的内容,无返回值。
7、Range.extractContents()
把Range
的内容从文档树中提取到文档片段中。返回的也是DocumentFragment类型。
例1-8:
|
|
注意⚠️:如果Range对象跨标签,extractContents方法会返回补齐后的标签,比如<p>abc</p><span>efg</span>
如果选中c</p><span>e
的话,返回的的DocumentFragment中内容是:<p>c</p><span>e</span>
。
8、Range.insertNode()
在起点处插入节点。该方法接受一个参数,newNode,要插入的节点。
例1-9:
|
|
newNode可以是documentFragment类型的节点。
9、Range.surroundContents
接收一个Node类型的参数newNode。把Range
对象的内容移动到newNode中,并且,将newNode放入当前Range
对象的开始位置。包裹之后,Range
对象的边界在也会受newNode影响。
例1-10:
|
|
An exception will be thrown, however, if the
Range
splits a non-Text
node with only one of its boundary points. That is, unlike the alternative above, if there are partially selected nodes, they will not be cloned and instead the operation will fail.
其他一些方法:
10、Range.compareBoundaryPoints()
接受两个参数,第一个参数是how,第二个参数是Range
类型的sourceRange,指示当前Range
的相应边界点是否分别在sourceRange的对应边界点之前,等于或者之后。返回一个数字类型的值,-1,0,1。具体参数配置,请移步MDN Range.compareBoundaryPoints()
例1-11:
|
|
11、Range.clone()
返回拥有和原 Range
相同端点的克隆 Range
对象。
The returned clone is copied by value, not reference, so a change in either
Range
does not affect the other.返回的clone对象拷贝了值,不是返回原有range的引用,所以改变其中的一个range不会影响另外一个。
12、Range.detach()
从使用状态释放 Range
,此方法用于改善性能。
Subsequent attempts to use the detached range will result in a
DOMException
being thrown with an error code ofINVALID_STATE_ERR
.随后尝试使用已经释放的range会抛出一个DOMException的异常,错误状态码:INVALID_STATE_ERR。
13、Range.toString()
把Range
对象的内容作为字符串返回。alert( Range对象 )
默认调用的就是toString方法,弹出选中的内容。
还有一些实验性的方法,不推荐在生产环境中使用:
14、Range.getBoundingClientRect()
返回一个 ClientRect
对象,该对象限定了选定的文档对象的内容,该方法返回了一个矩形,这个矩形包围了该文档对象中所有元素的边界矩形集合。
This is method is useful for determining the viewport coordinates of the cursor or selection inside a text box. See
Element.getBoundingClientRect()
for the details on the returned value.
当想获取视图窗口的坐标系时,这个方法还是挺有用的。返回当前Range
的top,right,bottom,left,width,height。
15、Range.getClientRects()
返回一系列包含ClientReact对象的list。每个ClientRect对象代表在当前Range
对象里的元素的ClientRect。
The Range.getClientRects()** **method returns a list of
ClientRect
objects representing the area of the screen occupied by the range. This is created by aggregating the results of calls toElement.getClientRects()
for all the elements in the range.
16、其他方法,比如:Range.compareNode()
、Range.comparePoint()
、Range.createContextualFragment()
、Range.intersectsNode()
、Range.isPointInRange()
都是Gecko 内核的方法,可移步MDN Web Api接口 Range
[^2]: 当range的开始节点或者结束节点不是文本节点的时候,偏移量代表想要选中的子节点个数的数字,不能超过子节点的长度,MDN上解释如下:If the endNode is a Node of type Text, Comment, or CDATASection, then endOffset is the number of characters from the start of endNode. For other Node types, endOffsetis the number of child nodes between the start of the endNode.
参考资料