关于selection和range的一些认识

因为最近做了大量的编辑器相关的需求,必不可少的认识了这两个概念。刚开始接触这两个概念的同学肯定还是会蒙圈的,冉义分不清楚这两个是干啥的。今天就来大致的分列一下他们的api和功能。

selection对象

首先,一个可编辑的区域,用户在操作时,是通过selection对象来进行操作的。当你选中一块区域,或者游标聚焦一个点时,都会创建一个selection对象。这时候,你可以通过document.getSelection()来取得这时候创建的对象。我们可以来看一些selection中的内容。

如上文本中的选区获得的selection中,包含了archorNode,baseNode,extentNode,focusNode这几个节点和一些属性,我们一个个来讲。

archnorNode(只读)

返回该选区起点所在的节点(Node)。比如,一个选中区由滑鼠事件产生,那archnorNode就是滑鼠按下时所在的节点。

archnorOffset(只读)

返回一个数字,其表示的是选区起点在anchorNode 中的位置偏移量。

  1. 如果 anchorNode 是文位元组点,那么返回的就是从该文位元组点的第一个字开始,直到被选中的第一个字之间的字数(如果第一个字就被选中,那么偏移量为零)。
  2. 如果 anchorNode 是一个元素,那么返回的就是在选区第一个节点之前的同级节点总数。(这些节点都是 anchorNode 的子节点)

如图上,archnorNode是当前的textnode节点,archnorOffset为向右偏移了12

focueNode(只读)

返回该选区终点所在的节点。

focueOffset(只读)

返回一个数字,其表示的是选区终点在focusNode 中的位置偏移量。

  1. 如果 focusNode 是文位元组点,那么选区末尾未被选中的第一个字,在该文位元组点中是第几个字(从0开始计),就返回它。
  2. 如果 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。

参数结构示意图:

推荐阅读:

相关文章