模拟实现 Range 的 insertNode() 方法
作者:怿飞 来源:怿飞blog 发布时间:2010-11-30 21:39:00
基本非 IE 的浏览器都支持 DOM Level2 中的 Range,而 IE 中仅有自己的简单处理方法(Text Rang)。
扩展阅读:
《Javascript标准DOM Range操作(1)》(节选自《JavaScript 高级程序设计》)
《Javascript标准DOM Range操作(2)》(节选自《JavaScript 高级程序设计》)
《Javascript标准DOM Range操作(3)》(节选自《JavaScript 高级程序设计》)
而 IE 下的 Text Rang 主要用来处理文本,并非 DOM 节点,那如何在 IE 下模拟 DOM Level2 中的 Range 呢?
根据规范的 API,我们需要模拟下述属性和方法:
function zRange() {
// Inital states
this.startContainer = document;
this.startOffset = 0;
this.endContainer = document;
this.endOffset = 0;
this.commonAncestorContainer = document;
this.collapsed = true;
// Range constants
this.START_TO_START = 0;
this.START_TO_END = 1;
this.END_TO_END = 2;
this.END_TO_START = 3;
}
zRange.prototype = {
// Public methods
setStart : function(node, offset){},
setEnd : function(node, offset){},
setStartBefore : function(node){},
setStartAfter : function(node){},
setEndBefore : function(node){},
setEndAfter : function(node){},
collapse : function(toStart) {},
selectNode : function(node) {},
selectNodeContents : function(node){},
deleteContents : function() {},
extractContents : function(){},
cloneContents : function() {},
surroundContents : function () {},
insertNode : function(node) {},
cloneRange : function() {},
detach : function() {},
compareBoundaryPoints : function (how, sourceRange) {},
constructor : zRange
}
我们还可以看一组使用 Range 方法和属性的统计数据,对于 2/8 原则的实践或许有帮助:
/**
* Resource reference : http://kb.operachina.com/node/147
*
* collapse 51,435
* setStartBefore 43,138
* setStartAfter 40,270
* selectNodeContents 37,027
* collapsed 12,862
* selectNode 4,636
* deleteContents 3,935
* setStart 3,171
* startOffset 3,150
* setEnd 3,086
* detach 2,732
* startContainer 2,659
* endOffset 2,647
* insertNode 2,321
* cloneContents 2,261
* endContainer 2,236
* cloneRange 1,993
* setEndAfter 1,911
*
*/
下面我们简单讨论一下 Range 的 insertNode() 方法的模拟实现:
The insertNode() method inserts the specified node into the Range’s context tree. The node is inserted at the start boundary-point of the Range, without modifying it.
If the start boundary point of the Range is in a Text node, the insertNode operation splits the Text node at the boundary point. If the node to be inserted is also a Text node, the resulting adjacent Text nodes are not normalized automatically; this operation is left to the application.
The Node passed into this method can be a DocumentFragment. In that case, the contents of the DocumentFragment are inserted at the start boundary-point of the Range, but the DocumentFragment itself is not. Note that if the Node represents the root of a sub-tree, the entire sub-tree is inserted.
从上面的引用得知 insertNode() 方法用来在选区的开头插入节点,固我们先获取 Range 对象的 startContainer(Range 是从那个节点中开始的,即选区中第一个节点的父节点) 和 startOffset(在 startContainer 中 Range 开始的偏移位置) 属性。
var sc = this.startContainer,
so = this.startOffset;
注: 如果 startContainer 是文本节点、注释节点或者是 CData 节点,startOffset 是指 Range 开始前的字符数,否则,偏移是 Range 中的第一个节点在其父节点中的索引。
接下来,我们需将情况分为下面两种:
第一种情形:startContainer 节点为文本节点、注释节点或者是 CData 节点。“startOffset 是指 Range 开始前的字符数”。
如果 startOffset 等于 0,则表示 Range 是从 startContainer 起始位置开始,应将 node 插入到 startContainer 节点之前
sc.parentNode.insertBefore(node, sc);
如果 startOffset 大于等于 startContainer 本身的节点长度,则表示 Range 是从 startContainer 末尾位置开始,应将 node 插入到 startContainer 节点之后,即如果存在下一节点,则插入到下一之前
sc.parentNode.insertBefore(node, sc.nextSibling);
,如果不存在下一节点,则加入到 startContainer父节点最后sc.parentNode.appendChild(node);
如果 startOffset 在 startContainer 本身的节点长度之内,我们通过
oSplitNode = object.splitText( [iIndex])
将节点在startOffset 一分为二nn = sc.splitText(so);
,分为两个节点,则应将 node 插入到新生成的第二个节点之前sc.parentNode.insertBefore(node, nn);
。The text node that invokes the splitText method has a nodeValue equal to the substring of the value, from zero to iIndex. The new text node has a nodeValue of the substring of the original value, from the specified index to the value length. Text node integrity is not preserved when the document is saved or persisted.
if (so===0) {
// At the start of text
sc.parentNode.insertBefore(node, sc);
} else if (so >= sc.nodeValue.length) {
// At the end of text
if (ns) {
sc.parentNode.insertBefore(node, sc.nextSibling);
} else {
sc.parentNode.appendChild(node);
}
} else {
// Middle, need to split
// http://msdn.microsoft.com/zh-cn/library/ms536764.aspx
nn = sc.splitText(so);
sc.parentNode.insertBefore(node, nn);
}
第二种情形:startContainer 节点为非 第一种情形中的节点。“偏移是 Range 中的第一个节点在其父节点中的索引”。获取 startContainer 中子节点偏移量为 startOffset 的节点 cn = sc.childNodes[so];
,如果存在,则按照 insertNode 方法的定义,应将 node 插入到该节点之前 sc.insertBefore(node, cn);
,如果不存在,即 startOffset 大于等于 sc.childNodes.length,则应将 node 插入到 startContainer 的子节点最后sc.appendChild(node);
。
if (sc.childNodes.length > 0) {
cn = sc.childNodes[so];
}
if (cn) {
sc.insertBefore(node, cn);
} else {
sc.appendChild(node);
}
详细代码实现如下:
zRange.prototype.insertNode = function(node) {
var sc = this.startContainer,
so = this.startOffset,
p = sc.parentNode,
ns = sc.nextSibling,
nn, cn;
// 如果节点是 TEXT_NODE 或者 CDATA_SECTION_NODE
if ((sc.nodeType === 3 || sc.nodeType === 4 || sc.nodeType === 8 ) && sc.nodeValue) {
if (so === 0) {
// At the start of text
p.insertBefore(node, sc);
} else if (so >= sc.nodeValue.length) {
// At the end of text
if (ns) {
p.insertBefore(node, ns);
} else {
p.appendChild(node);
}
} else {
// Middle, need to split
// http://msdn.microsoft.com/zh-cn/library/ms536764.aspx
nn = sc.splitText(so);
p.insertBefore(node, nn);
}
} else {
if (sc.childNodes.length > 0) {
cn = sc.childNodes[so];
}
if (cn) {
sc.insertBefore(node, cn);
} else {
sc.appendChild(node);
}
}
}
剩下的方法,大家可以尝试着去模拟一把,其实并不复杂,也许会其乐无穷,呵呵
猜你喜欢
- 四、 用户注册(reg.php)在看用户注册之流程之前,我把表的用途做个简单说明,现在只是大概的说明,后面我们再仔细了解,大家可以记下这个说
- 钉钉设置机器人首先在钉钉设置钉钉机器人群设置—> 智能群助手—>添加机器人—>自定义添加完成,得到一个Webhook AP
- var obj = document.getElementByIdx_x(”testSelect”); //定位idvar index =
- 一开始用phpMyAdmin来执行,后来出现一堆错误,后来去掉了begin,end之后可以正常执行,但要执行存储过程,在phpMyAdmn中
- 如下所示:#随机数的使用import random #导入randomrandom.randint(0,9)#制定随机数0到9i=rando
- 1、说明PyG2Plot 原理其实非常简单,其中借鉴了 pyecharts 的实现,但是因为蚂蚁金服的 G2Plot 完全基于可视分析理论的
- 1. vscode相关配置w3c school 手册: https://www.w3school.com.cn/html/inde
- 今天发现一个很好用二维数组排序的php方法,usort,推荐给大家,以后二维数组里面,要按照一个字段的值排序用这个方法简单高效,例如下面的数
- 1、官网下载地址在官网找到你想安装的版本 官网地址:https://www.python.org/并且选择下载windows版本目前最新的版
- 准备工作创建一个应用添加应用到配置创建一个html编写视图函数from django.shortcuts import render# Cr
- select UNIX_TIMESTAMP(ADDDATE(NOW(),INTERVAL -60 DAY))首先根据now()获得当前时间使
- 前言笔者最近正在学习Pandas数据分析,将自己的学习笔记做成一套系列文章。本节主要记录Pandas中数据的合并(concat和append
- python 爬虫解决403禁止访问错误在Python写爬虫的时候,html.getcode()会遇到403禁止访问的问题,这是网站对自动化
- 我们都知道 vue-router 的动态路由匹配 对组件是原地复用的策略,需要我们在组件中根据不同的 $route 参数展示不同的数据,这在
- 什么是字符串格式化,为什么需要这样做?我们有时候刷抖音/B站看到封面很好看,但是进入直播发现,不过如此!想必主播通过某种方式把输出转换为读者
- 最近一直在研究 Javascript 相关的技术。在《Javascript 高级程序设计》有篇章节着重阐述了优
- 一、python批量查询练习通过接口批量查询该ip是否属于指定接口:import requestsimport jsonif __name_
- 一、mongodb安装在官网下载适应于自己平台的mongodb,在此安装环境为Windows7-64bit下载完成后直接安装,连续点击nex
- 在附加数据库后查看不了数据库关系图,也无法建立数据库关系图 我的解决方法如下: 1、设置兼容级别为90(2005为90)(2000为80)
- 先写一个SQLSELECT DISTINCT from_idFROM codWHERE cod.from_id NOT IN (37, 56