range 标准化之获取


Posted in Javascript onAugust 28, 2011

w3c range

range 用来表示用户的选择区域,这块选择区域由两个边界位置界定,而位置则由其容器以及偏移量构成,称作 container 与 offset .如下是一个简单的位置示例:

<p><span>文字</span>^<span>文字</span></p>

其中 ^ 表示一个位置,则 container 为父节点 p,offset即为相对于父节点的偏移量为1。需要注意的是当container为元素节点时,其偏移量单元为节点,即从容器的第一个子节点到当前位置处所经历的子节点数。

相应的,container也可以是文字节点,这时container 为 textnode ,而offset则为从该textnode到当前位置所经历的 utf-16 字符个数(意味着,中文和英文计数一样,不是字节计数)。如

<p><span>文字</span><span>01234^567</span></p>

上例中 container 为 "01234567" 这个文字节点,而offset 则是从该文字节点第一个字符到当前位置所经历的字符个数即5.
ie range

简要介绍一下,具体请查阅 msdn

ie range 没有清晰的容器与偏移量的概念,但是基本思想和 w3c range 一样,具备同等的表达能力,分为 textrange 与 controlrange,包含一系列的方法。

textrange 不是从字面意义上的单纯文本,而是表示用户的选择区域内容(可用其htmlText获取完整内容),其操作方式大多是以文字为单元而不是 dom 树节点。

controlrange 字面意思表示获取选择 控件 ,实际是有些元素(div,img,object...)处于可编辑状态时,经单击可将整个元素选中。

ie 标准化之获取

从上面介绍可见,w3c 的range更规范,更清晰,其 container 与 offset 的概念更直观,当我们需要对range关联dom节点进行操作时,无疑 container ,offset是必不可少的,而 ie range 则没有显示的提供获取这两个关键变量的方法, 前面说过 ie range 其实具备 w3c range 等价的功能,那么就可以结合其提供的一系列方法推导出这两个变量。

范围对象获取:
范围对象有两种获取方式:

1.从当前选择区域获得 range ,可调用

document.selection.createRange()

方法根据目前选择,返回 TextRange 或 ControlRange 实例。

2.从元素创建 range ,可调用

oControlRange = object.createControlRange()

Js代码
oTextRange = object.createTextRange()

前一种 之能在 body,element上调用,而后一种 可在大多数元素上调用。调用后则该范围完全覆盖调用元素。相当于 moveToElementText 。

textrange 标准化:

首先介绍下用到的几个方法:

collapse :根据参数将结束位置重合到开始位置(true)或开始位置重合到结束位置(false)。

parentElement :获取包围选择区域的元素节点,如下例调用后得到span节点。

<span>文^字</span>

moveToElementText (Node a) :将选择区域变更为 a ,起始位置为 a 的前后,如作用到如下 span 节点后:
^<span>文字</span>^

range1.compareEndPoints('XxToYy',range2) :xx,yy 可以为 Start 或 End,取 range1 的 xx 位置和 range2 的 yy 位置比较,根据前后顺序返回-1,1,0表示重合。

range1.setEndPoint("XxToYy",range2) :xx,yy 可以为 Start 或 End,将range1的xx位置设为range2的yy位置。

转化:

有了上面的5个方法就可以开始我们的标准化第一步:获取位置,首先给出操作例子:

 

range 标准化之获取


(绿块表示文字节点,注意:正常手工编写页面情况下不会出现两个相邻的文字节点,这里使用 splitText 强制分离 )


当我们将选择区域collapse后,可能有上述四个位置:1,2,3,4,其中 1,4 相邻元素节点最简单:


1,4位置 标准化:


1.根据 parentElement 得到包含位置的节点 p ,即为该位置的container

2.对container的所有元素子节点,一一验证是否和已知位置相邻,验证方法即为:通过 moveToElementText 新建range包围子节点,再通过 compareEndPoints 比较是否新建 range 的前后位置是否和当前位置重合:

range = range.duplicate(); 
range.collapse(start); 
var parent = range.parentElement(), 
siblings = parent.childNodes; 
for (var i = 0; i < siblings.length; i++) { 
var child = siblings[i]; 
if (child.nodeType == 1) { 
var tr = range.duplicate(); 
tr.moveToElementText(child); 
//子元素节点开始位置比较 
var comparisonStart = tr.compareEndPoints('StartToStart', range), 
//子元素节点结束位置比较 
comparisonEnd = tr.compareEndPoints('EndToStart', range); 
//开头已经在当前位置后面,没必要继续比了 
if (comparisonStart > 0) break; 
else if (!comparisonStart || comparisonEnd == 1 && comparisonStart == -1) return { 
container: parent, 
offset: i 
}; 
else if (!comparisonEnd) return { 
container: parent, 
offset: i + 1 
}; 
} 
}

2,3位置 标准化:
2 表示位置在两个文字节点之间,container为 p ,由于moveToElementText 无法作用文字节点,则只能另想他法。
3 表示位置处于文本节点中间,此时 container 为文本节点,而 offset 则要数字符数了。
1.当到达 1 位置时,停止。
2.新建range ra,开始位置为 1,结束位置为2或3,取得 ra的所有字符数 ra_textlength,对从位置1开始往右的每个文字节点,从 ra_textlength 中减去其长度(data.length),当 ra_textlength 为0时,代表当前位置为2,而当前经过的文字节点数目即为offset。
当 ra_textlength 为负数时,表示当前位置为3, 当前的文字节点即为位置3 的container,ra_textlength 的上一个值则是offset。
示例:
<p id="test"><strong>粗体</strong>普通|文字<i>斜体</i></p> 
<script> 
document.getElementById("test").childNodes[1].splitText(2); 
</script>

标准化 demo

 

 

controlrange 标准化

 

controlrange 就很简单了,由 item(index) 方法得到选择元素,结合其parentNode 就可以得到标准化表示了。

 


PS : 关于输入框的范围读取


由于规范规定输入框的选择区域和页面选择区域是分离的,输入框的选择区域有不同的获取方式 (IE 基本相同)。

Javascript 相关文章推荐
解决遍历时Array.indexOf产生的性能问题
Jul 03 Javascript
jQuery创建平滑的页面滚动(顶部或底部)
Feb 26 Javascript
Javascript浮点数乘积运算出现多位小数的解决方法
Feb 17 Javascript
less简单入门(CSS 预处理语言)
Mar 08 Javascript
Javascript快速实现浏览器系统通知
Aug 26 Javascript
解决Jquery下拉框数据动态获取的问题
Jan 25 jQuery
Javascript的console['']常用输入方法汇总
Apr 26 Javascript
JS实现获取自定义属性data值的方法示例
Dec 19 Javascript
JS浅拷贝和深拷贝原理与实现方法分析
Feb 28 Javascript
vue路由拦截器和请求拦截器知识点总结
Nov 08 Javascript
小程序实现按下录音松开识别语音
Nov 22 Javascript
Promise静态四兄弟实现示例详解
Jul 07 Javascript
dojo学习第一天 Tab选项卡 实现
Aug 28 #Javascript
js中设置元素class的三种方法小结
Aug 28 #Javascript
IE6、IE7中setAttribute不支持class/for/rowspan/colspan等属性
Aug 28 #Javascript
IE6、IE7中获取Button元素的值的bug说明
Aug 28 #Javascript
JavaScript 选中文字并响应获取的实现代码
Aug 28 #Javascript
js预载入和JavaScript Image()对象使用介绍
Aug 28 #Javascript
jquery 查找iframe父级页面元素的实现代码
Aug 28 #Javascript
You might like
用PHP的ob_start() 控制您的浏览器cache
2009/08/03 PHP
PHP三元运算的2种写法代码实例
2014/05/12 PHP
PHP不用递归遍历目录下所有文件的代码
2014/07/04 PHP
php实现的通用图片处理类
2015/03/24 PHP
php将日期格式转换成xx天前的格式
2015/04/16 PHP
php htmlentities()函数的定义和用法
2016/05/13 PHP
Display SQL Server Login Mode
2007/06/21 Javascript
js类中获取外部函数名的方法
2007/08/19 Javascript
jquery 实现checkbox全选,反选,全不选等功能代码(奇数)
2012/10/24 Javascript
(跨浏览器基础事件/浏览器检测/判断浏览器)经验代码分享
2013/01/24 Javascript
使用Math.floor与Math.random取随机整数的方法详解
2013/05/07 Javascript
jQuery 删除/替换DOM元素的几种方式
2014/05/20 Javascript
input:checkbox多选框实现单选效果跟radio一样
2014/06/16 Javascript
JavaScript中的setMilliseconds()方法使用详解
2015/06/11 Javascript
JS实现超精简响应鼠标显示二级菜单代码
2015/09/12 Javascript
AngularJS 中使用Swiper制作滚动图不能滑动的解决方法
2016/11/15 Javascript
js通过指定下标或指定元素进行删除数组的实例
2017/01/12 Javascript
mpvue中配置vuex并持久化到本地Storage图文教程解析
2018/03/15 Javascript
记录vue做微信自定义分享的一些问题
2019/09/12 Javascript
vue中keep-alive内置组件缓存的实例代码
2020/04/16 Javascript
详细分析vue表单数据的绑定
2020/07/20 Javascript
[01:32]DOTA2次级联赛——首支职业女子战队选拔赛全记录
2014/10/23 DOTA
python实现随机密码字典生成器示例
2014/04/09 Python
Python实现的简单文件传输服务器和客户端
2015/04/08 Python
Python实现提取文章摘要的方法
2015/04/21 Python
OpenCV2.3.1+Python2.7.3+Numpy等的配置解析
2018/01/05 Python
Python实现删除时保留特定文件夹和文件的示例
2018/04/27 Python
python验证身份证信息实例代码
2019/05/06 Python
Python字符串大小写转换拼接删除空白
2019/09/19 Python
Python:合并两个numpy矩阵的实现
2019/12/02 Python
python利用opencv实现颜色检测
2021/02/23 Python
新西兰领先的内衣店:Bendon Lingerie新西兰
2018/07/11 全球购物
潘多拉珠宝美国官方网站:Pandora US
2020/06/18 全球购物
会计求职简历自我评价
2015/03/10 职场文书
Win11开始菜单添加休眠选项
2022/04/19 数码科技
Redis主从复制操作和配置详情
2022/09/23 Redis