关于selection和range的一些认识
关于selection和range的一些认识
因为最近做了大量的编辑器相关的需求,必不可少的认识了这两个概念。刚开始接触这两个概念的同学肯定还是会蒙圈的,冉义分不清楚这两个是干啥的。今天就来大致的分列一下他们的api和功能。
selection对象
首先,一个可编辑的区域,用户在操作时,是通过selection对象来进行操作的。当你选中一块区域,或者游标聚焦一个点时,都会创建一个selection对象。这时候,你可以通过document.getSelection()来取得这时候创建的对象。我们可以来看一些selection中的内容。
如上文本中的选区获得的selection中,包含了archorNode,baseNode,extentNode,focusNode这几个节点和一些属性,我们一个个来讲。
archnorNode(只读)
返回该选区起点所在的节点(Node
)。比如,一个选中区由滑鼠事件产生,那archnorNode就是滑鼠按下时所在的节点。
archnorOffset(只读)
返回一个数字,其表示的是选区起点在anchorNode
中的位置偏移量。
- 如果 anchorNode 是文位元组点,那么返回的就是从该文位元组点的第一个字开始,直到被选中的第一个字之间的字数(如果第一个字就被选中,那么偏移量为零)。
- 如果 anchorNode 是一个元素,那么返回的就是在选区第一个节点之前的同级节点总数。(这些节点都是 anchorNode 的子节点)
如图上,archnorNode是当前的textnode节点,archnorOffset为向右偏移了12
focueNode(只读)
返回该选区终点所在的节点。
focueOffset(只读)
返回一个数字,其表示的是选区终点在focusNode
中的位置偏移量。
- 如果 focusNode 是文位元组点,那么选区末尾未被选中的第一个字,在该文位元组点中是第几个字(从0开始计),就返回它。
- 如果 focusNode 是一个元素,那么返回的就是在选区末尾之后第一个节点之前的同级节点总数。
isCollapsed
返回一个布尔值,用于判断选区的起始点和终点是否在同一个位置。
rangeCount
返回该选区所包含的连续范围的数量。
type(非正式,测试属性)
None
: 当前没有选区Caret
: 选区被折叠,isCollapsed为true时(caret被放在一些文本中,但是没有范围range被选中,如一个游标)Range
:一个range存在
除了这些只读的属性,selection还暴露了一些api用以操作选中区域。
sel.getRangeAt(index)
range = sel.getRangeAt(index)
返回一个包含当前选区内容的区域对象(range)。这里的index的范围是连续选区的个数,及index不能比rangeCount大,否则它会报错。
collapse,collapseToStart,collapseToEnd
sel.collapse()
方法可以收起当前选区到一个点。文档不会发生改变。如果选区的内容是可编辑的并且焦点落在上面,则游标会在该处闪烁。这主要的作用是形成一个游标,可让选中区聚焦。collapseToStart和collapseToEnd其实做了一样的事,只不过一个折叠到选区的开始,一个折叠到选区的结束。
addRange
sel.addRange(Range)
添加一个新的选区range到当前selection中。
removeRange
sel.removeRange(range)
移除一个特定的range。
Range对象
range对象,代表一个连续的选中区域。range可以直接从selection.getRangeAt(index)的方式来拿,可以直接document.createRange() 的方式来创建。
先来看几个只读的range的属性:
Range.collapsed
返回一个用于判断 Range
起始位置和终止位置是否相同的布尔值。
Range.commonAncestorContainer
返回包含 startContainer
和 endContainer 的最深的节点。
Range.endContainer
返回包含 Range
终点的节点。
Range.endOffset
返回 endContainer 中表示Range终点位置的数字。
Range.startContainer
返回包含 Range
开始的节点。
Range.startOffset
返回 startContainer 中表示 Range
起始位置的数字。
这些主要是一些节点的信息。
然后看几个常用的api:
Range.setStart() 和Range.setEnd()
用于设置Range
的开始位置和结束位置。使用:
range.setStart(startNode,startOffset)range.setEnd(startNode,startOffset)
这里的node和offset分别为需要设置的起始点(结束点)所在的节点和偏移量。
range.setStartBefore(),Range.setStartAfter(),Range.setEndBefore(),range.setEndAfter()几个方法都是以其它节点为基准,来设置 Range 的起始点或者终点。
Range.selectNode()
这个方法主要用于将range包含在某个特定node节点下
editor=document.getElementById("editor")range=document.createRnage();range.selectNode("editor")
这样就创建了一个range并将range包含了整个id为editor所在的元素。
Range.selectNodeContents()
这个方法主要用于将range包含在某个特定node节点内,但是不包含node,只包含node 节点内的内容。
编辑方法
通过以下方法,可以从 Range 中获得节点,改变 Range 的内容。
range.cloneContents()
返回 Range 当中节点的文档片段(DocumentFragment)。
Range.deleteContents()
从文档(Document)中移除 Range 中的内容。这个方法会把range中的内容清空。可用于一些特定的删除操作。某些特定的情况下,如前端与客户端的交互中,前端的编辑区域失去焦点后,在iOS10下,不点击编辑区的话无法再次聚焦,这时,可以先记下之前的range,等再次需要聚焦前端时用deleteContents来代替execCommand(delete)的方案删除特定字元。
//记下之前的range所在nodevar beforeNode=sel.getRangeAt(0);var focusNode=beforeNode.focusNode;/*******apphost唤起native事件,前端失去焦点。*******//*******apphost事件结束。*******/var range=focusNode//根据node和Offset删除需要删除的内容range.setStart(node,offset);range.setEnd(node,offset);range.deleteContents();
Range.extractContents()
把 Range 的内容从文档树移动到文档片段中。
Range.insertNode()
在 Range 的起点处插入节点。可以用于向document中插入一些内容,比如像<div contenteditable="true"></div>的元素在使用普通的execCommand(insertHtml)的方法来粘贴时,会丢掉contenteditable属性,而使用选区的方法则可以保留。
Range.surroundContents()
将Range 的内容移动到一个新的节点中。
Range.getBoundingClientRect()
方法返回元素的大小及其相对于视口的位置。如下是选区一些参数的获取(宽高和坐标)。不过在ios下,以游标作为选区时,取到 的这些参数都是0。
参数结构示意图:
推荐阅读: