解密jQuery内核 DOM操作的核心buildFragment
文档碎片是什么
http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-B63ED1A3
DocumentFragment is a "lightweight" or "minimal" Document object. It is very common to want to be able to extract a portion of a document's tree or to create a new fragment of a document
参考标准的描述,DocumentFragment是一个轻量级的文档对象,能够提取部分文档的树或创建一个新的文档片段
换句话说有文档缓存的作用
createDocumentFragment有什么作用
多次使用节点方法(如:appendChild)绘制页面,每次都要刷新页面一次。效率也就大打折扣了,而使用document_createDocumentFragment()创建一个文档碎片,把所有的新结点附加在其上,然后把文档碎片的内容一次性添加到document中,这也就只需要一次页面刷新就可。
DocumentFragment类型
在所有节点类型中,只有DocumentFragment在文档中没有对应的标记。DOM规定文档片段(documentfragment)是一种”轻量级“的文档,可以包含和控制节点,但不会像完整的文档那样占用额外资源。DocumentFragment节点具有下列特征:
- nodeType的值为11
- nodeName的值为“#document-fragment”
- nodeValue的值为null
- parentNode的值为null
- 子节点可以是Element、ProcessingInstruction、Comment、Text、CDATASection或EntityReference
虽然不能把文档片段直接添加到文档中,但可以将它作为一个“仓库”来使用,即可以在里面保存将来可能会添加到文档中的节点。要创建文档片段,可以使用document.createDocumentFragment()方法,如下所示:
var fragment = document.createDocumentFragment();
文档片段继承了Node的所有方法,通常用于执行那些针对文档的DOM操作。如果将文档中的节点添加到文档片段中,就会从文档树中再看到该节点。添加到文档片段中的新节点同样也不属于文档树。可以通过appendChild()或insertBefore()将文档片段中内容添加到文档中。在将文档片段作为参数传递给这两个方法时,实际上只会将文档片段的所有子节点添加到相应的位置上;文档片段本身永远不会称为文档树的一部分
http://www.w3cmm.com/dom/documentfragment.html
createElement与createDocumentFragment
createElement是创建一个新的节点,createDocumentFragment是创建一个文档片段
DocumentFragment 接口表示文档的一部分(或一段)。更确切地说,它表示一个或多个邻接的 Document 节点和它们的所有子孙节点。
DocumentFragment 节点不属于文档树,继承的 parentNode 属性总是 null。
不过它有一种特殊的行为,该行为使得它非常有用
即当请求把一个 DocumentFragment 节点插入文档树时,插入的不是 DocumentFragment 自身,而是它的所有子孙节点。这使得 DocumentFragment 成了有用的占位符,暂时存放那些一次插入文档的节点。它还有利于实现文档的剪切、复制和粘贴操作,尤其是与 Range 接口一起使用时更是如此
可以用 Document.createDocumentFragment() 方法创建新的空 DocumentFragment 节点。
也可以用 Range.extractContents() 方法 或 Range.cloneContents() 方法 获取包含现有文档的片段的 DocumentFragment 节点。
除此之外
createElement创建的元素可以使用innerHTML,createDocumentFragment创建的元素使用innerHTML并不能达到预期修改文档内容的效果,只是作为一个属性而已。两者的节点类型完全不同,并且createDocumentFragment创建的元素在文档中没有对应的标记,因此在页面上只能用js中访问到
createElement创建的元素可以重复操作,添加之后就算从文档里面移除依旧归文档所有,可以继续操作,但是createDocumentFragment创建的元素是一次性的,添加之后再就不能操作了
在之前domManip方法中提到的iNoClone多个节点操作需要克隆,就是因为文档碎片的特性引起的
大体了解了,我们看看jQuery对于节点操作的时候,加强版的文档碎片buildFragment
buildFragment
我们知道用文档碎片无非就是先创建
fragment = context.createDocumentFragment(),
然后把所有需要处理的dom节点给appendChild进去
buildFragment对于文档碎片的创建,可以看到被切分了2个部分
先看第一部分代码
收集节点元素
我们看一个参数,包含了 字符串,$对象
var $e = $('<span>e</span>'), $x = $('<span>x</span>');
inner.after(' ', $e, ' ', $x)
对应的buildFragment就需要针对传入elems的分解可以有三部分,引入一个nodes缓存起来
jQuery对象
if ( jQuery.type( elem ) === "object" ) {
// Support: QtWebKit
// jQuery.merge because core_push.apply(_, arraylike) throws
jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
文本类型
nodes.push( context.createTextNode( elem ) )
字符串HTML
将HTML代码赋值给一个DIV元素的innerHTML属性,然后取DIV元素的子元素,即可得到转换后的DOM元素、
tmp = tmp || fragment.appendChild( context.createElement("div") );
// Deserialize a standard representation
tag = ( rtagName.exec( elem ) || ["", ""] )[ 1 ].toLowerCase();
wrap = wrapMap[ tag ] || wrapMap._default;
tmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[ 2 ];
// Descend through wrappers to the right content
j = wrap[ 0 ];
while ( j-- ) {
tmp = tmp.lastChild;
}
// Support: QtWebKit
// jQuery.merge because core_push.apply(_, arraylike) throws
jQuery.merge( nodes, tmp.childNodes );
// Remember the top-level container
tmp = fragment.firstChild;
// Fixes #12346
// Support: Webkit, IE
tmp.textContent = "";
创建了一个临时的tmp元素(div),这样调用innerHTML方法,用来储存创建的节点的内容,fragment本身只是起到一个容器的作用,这点我们要记住了
但是jQuery引入了一个wrapMap,一个反序列化表示
用来干嘛的?
我们知道看jQuery创建元素类型可以是任意的,可以所以可以是是a,scrpit,tr,th,option等等
inner.after('<tr><tr>');
inner.after('<div><div>');
但是在并不是所有元素的的创建都是标准的,在不同浏览器下还是有区别,比如表格
比如在table中插入一行一列
var table = document.getElementsByTagName('table')[0];
var tr = document.createElement('tr');
var td = document.createElement('td');
var txt = document.createTextNode('haha');
td.appendChild(txt);
tr.appendChild(td);
table.appendChild(tr);
面代码在IE 6上是执行不成功的,大家可以试一下。在IE 8以上的浏览器都是好用的。
IE 6上失败的原因就是IE 6认为tr标签必须在tbody下面。也就是说,代码写成下面这样,就所有浏览器都OK了。
var table = document.getElementsByTagName('table')[0];
var tbody = document.createElement('tbody');
var tr = document.createElement('tr');
var td = document.createElement('td');
var txt = document.createTextNode('haha');
td.appendChild(txt);
tr.appendChild(td);
tbody.appendChild(tr);
table.appendChild(tbody)
所以如果是jQuery插入一个tr标签,就需要在内部做这样的处理工作了
inner.after('<tr><tr>');
wrapMap就是用来做适配的
tmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[ 2 ];
拼写出来的规则就是
innerHTML: "<table><tbody><tr></tr><tr></tr></tbody></table>"
具体有多少类似的问题我们看看

因为wrapMap容器打破了原来的排列组合所以tr节点位置需要重新定位
就那面这个tr,lastChild变成了table, 所以需要根据wrap[ 0 ]找到嵌套的层数
j = wrap[ 0 ];
while ( j-- ) {
tmp = tmp.lastChild;
}
因为fragment现在还不确定是最终的,因为node可能还有其他的节点,所以
fragment.textContent = "";
构建文档碎片
while ( (elem = nodes[ i++ ]) ) {
// #4087 - If origin and destination elements are the same, and this is
// that element, do not do anything
if ( selection && jQuery.inArray( elem, selection ) !== -1 ) {
continue;
}
contains = jQuery.contains( elem.ownerDocument, elem );
// Append to fragment
tmp = getAll( fragment.appendChild( elem ), "script" );
// Preserve script evaluation history
if ( contains ) {
setGlobalEval( tmp );
}
// Capture executables
if ( scripts ) {
j = 0;
while ( (elem = tmp[ j++ ]) ) {
if ( rscriptType.test( elem.type || "" ) ) {
scripts.push( elem );
}
}
}
}
处理第一种情况,如果元素和目标元素是相同的
http://bugs.jquery.com/ticket/4087
遍历每一个元素放入到文档碎片中
fragment.appendChild( elem )
还有种情况就是写入的是scrpit标签了,用的很少先跳过
最终返回fragment
解密jQuery内核 DOM操作的核心buildFragment的更多相关文章
- 解密jQuery内核 DOM操作的核心函数domManip
domManip是什么 dom即Dom元素,Manip是Manipulate的缩写,连在一起就是Dom操作的意思. .domManip()是jQuery DOM操作的核心函数 对封装的节点操作做了参数 ...
- 解密jQuery内核 DOM操作
jQuery针对DOM操作的插入的方法有大概10种 append.prepend.before.after.replaceWith appendTo.prependTo.insertBefore.in ...
- 解密jQuery内核 DOM操作方法(二)html,text,val
回顾下几组DOM插入有关的方法 innerHTML 设置或获取位于对象起始和结束标签内的 HTML outerHTML 设置或获取对象及其内容的 HTML 形式 看图对照区别 innerText 设置 ...
- 解密jQuery内核 样式操作
基础回顾 jQuery里节点样式读取以及设置都是通过.css()这个方法来实现的,本章通一下分解探究下jquery里这部分代码的实现 那么jQuery要处理样式的哪些问题? 先简单回顾下样式操作会遇到 ...
- jQuery的DOM操作详解
DOM(Document Object Model-文档对象模型):一种与浏览器, 平台, 语言无关的规则, 使用该接口可以轻松地访问页面中所有的标准组件DOM操作的分类 核心-DOM: DOM Co ...
- 第3章 jQuery的DOM操作
一. DOM 分为DOM核心,HTML-DOM和CSS-DOM 1.DOM核心 不专属与javascript. 获取对象:document.getElementsByTagName('div') 获 ...
- jQuery – 3.JQuery的Dom操作
3.1 JQuery的Dom操作 1.使用html()方法读取或者设置元素的innerHTML 2.使用text()方法读取或者设置元素的innerText 3.使用attr() ...
- js,jQuery和DOM操作的总结(二)
jQuery的基本操作 (1)遍历键值对和数组 , , , , , ]; $.map(arr, function (ele, index) { alert(ele + '===' + index); ...
- 03-老马jQuery教程-DOM操作
jQuery DOM操作 在没有jQuery之前,DOM的操作相对来说有点麻烦,尤其是DOM节点的搜索.目前我们已经学习了jQuery的选择器,接下带大家一块学习jQuery的DOM操作,jQuery ...
随机推荐
- ASP.NET下回车键的触发效果
在ASP.NET下,在客户端触发回车键,默认调用了页面中第一个button,这有时是非常头痛的,比如页面的第一个按键是注销键时,想想也够可怕了. .net提供设置默认回车键的属性,this.Form. ...
- linux shell输出带颜色文本
echo -e "\033[33;31m Color Text" - red echo -e "\033[33;32m Color Text" - green ...
- Git使用出错:Couldn‘t reserve space for cygwin‘s heap, Win32
今天使用Git在命令行下更新代码遇到了问题,起初觉得是自己安装某软件导致冲突,从网上搜索了一下找到类似问题,成功解决问题. 错误信息如下: E:\storm-sql>git pull origi ...
- linu for循环
用途说明 在shell中用于循环.类似于其他编程语言中的for,但又有些不同.for循环是Bash中最常用的语法结构. 常用格式 格式一 for 变量 do 语句 done 格式二 for 变量 in ...
- mssql查询列名中包含特定字段的列
CREATE TABLE itemdata_LANG ( itemno ) NOT NULL, itemname ), -- 产品名称 othername ), indications ), -- 适 ...
- SUBLIME 添加PHP控制台
原文地址:http://www.libenfu.com/sublime-%E6%B7%BB%E5%8A%A0php%E6%8E%A7%E5%88%B6%E5%8F%B0/ 点击工具 > 编译系统 ...
- JS代码将数据导入Excel
如果在别的浏览器中无法导入,尝试用IE浏览器 function AutomateExcelall(){try { oXL = new ActiveXObject('Excel.Application' ...
- c#中abstract与virtua、overridel的用法
1.abstract 抽象方法 ,virtual 虚方法 ,override 重载函数 父类A.m() 子类B.m() abstract的方法父类可以不实现,让子类去重写(重写=overwrite ...
- IntelliJ IDEA 15 激活码 正版 可离线激活
43B4A73YYJ-eyJsaWNlbnNlSWQiOiI0M0I0QTczWVlKIiwibGljZW5zZWVOYW1lIjoibGFuIHl1IiwiYXNzaWduZWVOYW1lIjoiI ...
- SQL Server中CROSS APPLY和OUTER APPLY的应用详解
SQL Server数据库操作中,在2005以上的版本新增加了一个APPLY表运算符的功能.新增的APPLY表运算符把右表表达式应用到左表表达式中的每一行.它不像JOIN那样先计算那个表表达式都可以, ...