一、Selection对象
1、简介
Selection
对象存储了用户在网页上选择的文本范围或者光标符号的位置等信息,代表网页中的文本选区,可能横跨多个元素,包含文本、图片等等。文本选区是由用户点击鼠标左键并拖拽鼠标选中页面内容产生的,也就是指页面上变成蓝底的那一部分内容。
浏览器兼容性:
2、属性和方法
在控制台输出一个Selection
对象结果如下:
其中常用属性有(只读):
anchorNode
:一个node节点,表示选区起点所在的节点,也就是鼠标左键按下时,所在的那个节点。
anchorOffset
:一个数字,表示选区起点在anchorNode
节点中的位置(从0开始)。
focusNode
:一个node节点,表示选区终点所在的节点,也就是鼠标左键。
focusOffset
:一个数字,表示选区终点在focusNode
中的位置(从0开始,到选区终点后的那个位置)。
isCollapsed
:一个布尔值,表示选区的起点和终点是否在同一个位置,也就是指是否选中网页内容,如果选区的起点和终点在一个位置,则表示没有选中任何内容。
rangeCount
:表示该选区内Range对象的数量,主流浏览器通常都为1(只有Gecko浏览器可以存在多个Range对象)
baseNode
与extentNode
:与anchorNode
和focusNode
相同,已经被MDN所移除,浏览器会逐渐放弃支持(Firefox已经移除)。
其他…
注意: anchor
指向用户开始选择的地方即按下鼠标左键的地方,focus
指向用户结束选择的地方即松开鼠标左键的地方,用户可能使用鼠标从左到右选择内容,也有可能从右到左选择内容,因此 anchor
可能位于focus
后面。
常用的方法有:
getRangeAt(index)
:获取当前选区包含的Range
对象,因为主流浏览器选区只能包含一个Range
,所以需要用getRangeAt(0)
来获取。
// 获取当前选区包含的Range对象
window.getSelection().getRangeAt(0);
addRange()
:将一个Range
对象加入到当前选区之中,但由于主流浏览器选区只能包含一个Range
,所以通常用于创建选区。在主流浏览器中,如果选区中已经存在Range
对象,然后继续通过该方法添加Range
对象,此时的操作是无效的。
// 创建range对象
var range = document.createRange();
// 添加到选区
window.getSelection().addRange(range);
removeRange(range)
:从当前选区移除一个Range
对象。
// 移除选区中的range对象
window.getSelection().removeRange(range);
removeAllRanges()
:将当前选区的所有的range
对象移除。
// 移除选区中的所有range对象
window.getSelection().removeAllRange();
deleteFromDocument()
:将当前选区的的内容从页面上删除,使用该方法删除网页内容时,如果要删除的内容是一个元素范围内的内容,则删除内容后,会在DOM中保留该元素存在,无论该元素内容是否为空。如果要删除的内容横跨多个元素,则删除内容后,会在DOM中保留起点所在元素和终点所在元素这两个元素,无论其内容是否为空。
// 在网页上删除选中的内容
window.getSelection().deleteFromDocument();
toString()
:获取当前选区的纯文本内容,只返回选区内的文字,不包括图片等其他内容。
// 获取选区内的文本内容
window.getSelection().toString();
containsNode(node,boolean)
:判断一个node节点是否包含在当前选区内,第二个参数代表部分包含是否算包含,默认为false,如果为true,则选区包含节点的一部分或全包含时,都算包含,返回true;如果为false,则只有选区全包含节点时,才算包含,返回true。
// node节点
<span id="test">1111111111111111111111111111111111111</span>
// 判断是否包含在选区内
window.getSelection().containsNode(document.querySelector("#test"), true)
其他…
3、获取方式
想要获取Selection
对象,我们只能通过window.getSeletion()
或document.getSeletion()
来获取,但我们应该在什么阶段去获取Selection
对象,得到需要的数据呢?个人认为比较合适的获取时机有两个:
① document
的selectionchange
事件中进行获取,因为该事件表示当前页面文档中用户操作的选区发生了变化,优点是只有在选区变化时才会触发;缺点是用户选中某段内容的过程中会多次触发,而不是在用户选择结束后触发,触发较为频繁,而且该事件只能绑定在document
上,面向的是整个页面,无法限定触发的区域范围。
// 监听整个页面的selectionchange事件
document.onselectionchange = () => {
// 获取Selection对象
console.log(window.getSelection());
};
② 在鼠标松开事件mouseup
中进行获取,该事件触发的场景分两种:鼠标点击和鼠标选中,我们只需要通过Selection对象的isColapsed
属性来判断是否选中页面内容来区分两种场景即可。优点是可以绑定在任一父元素上,从而限定触发范围在某一区域,而且在用户选中某段内容过程中不会触发,选中完成后才会触发。缺点是需要通过各种条件判断才能区分出用户选中页面内容时的操作。
// 监听限定区域的mouseup事件
<div class="parent" @mouseup="selectContent"></div>
// 对应事件
selectContent() {
// 过滤掉点击产生的鼠标松开事件
if (window.getSelection().isCollapsed) {
console.log("并未选中任何内容");
return;
}
// 获取Selection对象
console.log(window.getSelection());
}
二、Range对象
1、简介
Range
对象表示一个包含节点与文本的文档片段,可以与Selection
对象结合使用,Selection
对象选中的文本选区所对应的文档片段,就是一个Range
对象。
浏览器兼容性:
2、属性和方法
控制台输出Range
对象结果如下:
常用的方法有:
cloneContents()
:获取当前Range
对象对应的文档片段的复制片段,包含所有节点。
window.getSelection().getRangeAt(0).cloneContents()
cloneRange()
:复制当前Range
对象,是复制而并非引用,两者之间不会互相干扰。
window.getSelection().getRangeAt(0).cloneRange()
deleteContents()
:删除页面上当前Range
对象对应的文档片段,与Selection
的deleteFromDocument()
方法,作用相同。使用该方法删除网页内容时,如果要删除的内容是一个元素范围内的内容,则删除内容后,会在DOM中保留该元素存在,无论该元素内容是否为空。如果要删除的内容横跨多个元素,则删除内容后,会在DOM中保留起点所在元素和终点所在元素这两个元素,无论其内容是否为空。
window.getSelection().getRangeAt(0).deleteContents()
toString()
:获取当前Range
对象中的纯文本信息,与Selection
的toString()
方法,作用相同,只返回选区内的文字,不包括图片等其他内容。
window.getSelection().getRangeAt(0).toString()
setStart(node,offset)
:设置Range
对象的起始位置,node表示起始节点,offset表示在节点内的偏移量,如果节点是Text(文本节点)
、Comment(注释节点)
、CDATASection(CDATA 片段,仅用于XML中)
三种类型,则offset表示的是字符的偏移量,其他类型的节点,则offset表示的是子节点的偏移量,但要注意起始位置一定要在终点位置之前。
<p><span>1</span><span>1</span><span>1</span><span>1</span></p>
<p><span>1</span><span>1</span><span>1</span><span>1</span></p>
<p><span>1</span><span>1</span><span>1</span><span>1</span></p>
<script>
// 获取目标dom节点
const ps = document.querySelectorAll("p");
// 设置 Range 起始位置在第二个p标签的 第二个子节点
range.setStart(ps[1], 2);
</script>
setEnd(node,offset)
:设置Range
对象的终点位置,node表示终止节点,offset表示在节点内的偏移量,如果节点是Text(文本节点)
、Comment(注释节点)
、CDATASection(CDATA 片段,仅用于XML中)
三种类型,则offset表示的是字符的偏移量,其他类型的节点,则offset表示的是子节点的偏移量,但要注意终点位置一定要在起始位置之后。
// 设置 Range 结束位置在第三个p标签的 第三个子节点
range.setEnd(ps[2], 3);
getBoundingClientRect()
:返回一个DOMRect
对象(包含所有元素的最小矩形,即浏览器展示选中效果的矩形,包括 padding
和 border-width
),可以获取到当前Range
对象对应的文档片段在浏览器可视区域内的大小以及位置信息,包括: left
、top
、right
、bottom
、x
、y
、width
和 height
。
window.getSelection().getRangeAt(0).getBoundingClientRect()
selectNode(node)
:设置当前Range
对象对应的文档片段为:整个node及其所有内容节点。页面效果相当于设置选中某node节点。
window.getSelection().getRangeAt(0).selectNode(document.querySelector("#test"));
selectNodeContents(node)
:设置当前Range
对象对应的文档片段为:node的所有内容节点,但不包括node本身。页面效果与selectNode(node)
相同,主要区别在于:commonAncestorContainer
、endContainer
、startContainer
三条属性对应的节点是node本身,还是node的父节点。
window.getSelection().getRangeAt(0).selectNodeContents(document.querySelector("#test"));
insertNode(node)
:在当前Range
对象的起始位置插入一个node节点,如果起始位置是前后两个节点的分界位置,则插入的节点与前后两个节点同级,如果起始位置位于某个节点中间,则插入的节点会成为起始位置所在节点的子节点。
window.getSelection().getRangeAt(0).insertNode(document.querySelector("#test"));
intersectsNode(node)
:判断node节点与当前Range
对象是否有交集(部分相交或全包含),结果返回一个布尔值。
window.getSelection().getRangeAt(0).intersectsNode(document.querySelector("#test"));
其他…
3、获取方式
① document.createRange()
:该方法可以创建一个空的Range
对象,但在使用其大部分方法之前,需要通过setStart()
和serEnd()
方法来设置其文档片段的边界:
// 创建range对象
var range = document.createRange();
// 设置Range的起始位置
range.setStart(startNode, startOffset);
// 设置 Range 结束位置
range.setEnd(endNode, endOffset);
// 获取selection对象
const selection = window.getSelection();
// 将range对象添加到selection对象中,即添加页面选中效果
selection.addRange(range);
② new Range()
:该方法使用构造函数,返回一个新创建的Range
对象,但在使用其大部分方法之前,需要通过setStart()
和serEnd()
方法来设置其文档片段的边界:
// 获取目标dom节点
const ps = document.querySelectorAll("p");
// 创建 Range 对象
const range = new Range();
// 设置 Range 起始位置在第二个p标签的 第二个子节点
range.setStart(ps[1], 2);
// 设置 Range 结束位置在第三个p标签的 第三个子节点
range.setEnd(ps[2], 3);
// 获取selection对象
const selection = window.getSelection();
// 将range对象添加到selection对象中,即添加页面选中效果
selection.addRange(range);
③ window.getSelection().getRangeAt(0)
:该方法可以获取当前Selection
选区中的Range
对象。
// 获取当前Selection对象中的range对象
const range = window.getSelection().getRangeAt(0)
④ document.caretRangeFromPoint(float x, float y)
(非标准特性,不推荐使用):该方法可以根据当前视图中的x和y的位置获取对应的Range
对象,如果 x 或 y 不存在或者在视图外以及在没有文本节点的位置时,会返回null。但该方法获取的Range
对象起点和终点是重叠的,因为参数只是一个点的坐标,我们可以在这个点的基础上,通过setStart()
或setEnd()
方法,来扩展Range
对象的范围。
// 存在内容的位置
const range = document.caretRangeFromPoint(829.5859375, 583.4375);
console.log("根据位置获取的Range---", range);
// 此时获取的range对象的起点和终点是重叠在一起的 所以我们可以以这个点为基础 重新设置range对象的终点
range.setEnd(range.endContainer, 30);