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 相关文章推荐
jQuery 验证插件 Web前端设计模式(asp.net)
Oct 17 Javascript
jquery的$getjson调用并获取远程的JSON字符串问题
Dec 10 Javascript
jquery获取颜色在ie和ff下的区别示例介绍
Mar 28 Javascript
js处理php输出时间戳对不上号的解决方法
Jun 20 Javascript
基于jQuery实现在线选座之高铁版
Aug 24 Javascript
Javascript中的return作用及javascript return关键字用法详解
Nov 05 Javascript
[原创]javascript typeof id==='string'?document.getElementById(id):id解释
Nov 02 Javascript
JS操作input标签属性checkbox全选的实现代码
Mar 02 Javascript
vue2路由方式--嵌套路由实现方法分析
Mar 06 Javascript
Vue $emit()不能触发父组件方法的原因及解决
Jul 28 Javascript
Vue触发input选取文件点击事件操作
Aug 07 Javascript
原生js实现拖拽移动与缩放效果
Aug 24 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笔记之常用文件操作
2010/10/12 PHP
解析posix与perl标准的正则表达式区别
2013/06/17 PHP
php实现处理输入转义字符的代码
2015/11/08 PHP
PHP读取CSV大文件导入数据库的实例
2017/07/24 PHP
搜索附近的人PHP实现代码
2018/02/11 PHP
YII2框架中使用RBAC对模块,控制器,方法的权限控制及规则的使用示例
2020/03/18 PHP
Mootools 1.2教程(3) 数组使用简介
2009/09/14 Javascript
javascript 隐藏/显示指定的区域附HTML元素【legend】用法
2010/03/05 Javascript
jquery的ajax()函数传值中文乱码解决方法介绍
2012/11/08 Javascript
常见表单重复提交问题整理及解决方法
2013/11/13 Javascript
jquery事件重复绑定的快速解决方法
2014/01/03 Javascript
js 去掉空格实例 Trim() LTrim() RTrim()
2014/01/07 Javascript
jQuery级联操作绑定事件实例
2014/09/02 Javascript
jquery 实现两Select 标签项互调示例代码
2014/09/25 Javascript
原生js实现键盘控制div移动且解决停顿问题
2016/12/05 Javascript
提高Web性能的前端优化技巧总结
2017/02/27 Javascript
Windows下支持自动更新的Electron应用脚手架的方法
2018/12/24 Javascript
js中Generator函数的深入讲解
2019/04/07 Javascript
VUE 项目在IE11白屏报错 SCRIPT1002: 语法错误的解决
2020/09/27 Javascript
如何搭建一个完整的Vue3.0+ts的项目步骤
2020/10/18 Javascript
vue自定义树状结构图的实现方法
2020/10/18 Javascript
解决vue项目运行npm run serve报错的问题
2020/10/26 Javascript
vue router返回到指定的路由的场景分析
2020/11/10 Javascript
调试Python程序代码的几种方法总结
2015/04/28 Python
python比较两个列表大小的方法
2015/07/11 Python
TensorFlow 滑动平均的示例代码
2018/06/19 Python
python基于K-means聚类算法的图像分割
2019/10/30 Python
win10下python3.8的PIL库安装过程
2020/06/08 Python
python 如何调用 dubbo 接口
2020/09/24 Python
Under Armour澳大利亚官网:美国知名的高端功能性运动品牌
2018/02/22 全球购物
JoJo Maman Bébé爱尔兰官网:英国最受欢迎的精品母婴品牌
2020/12/20 全球购物
临床医师专业个人自我评价范文
2013/11/07 职场文书
营销总经理岗位职责范本
2014/09/02 职场文书
导游词之台湾安平古堡
2019/12/25 职场文书
阿里云ECS云服务器快照的概念以及如何使用
2022/04/21 Servers
深入理解MySQL中MVCC与BufferPool缓存机制
2022/05/25 MySQL