解密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 ...
随机推荐
- Python读取文件内容并将内容插入到SSDB中
import os import linecache import time from SSDB import SSDB ssdb = SSDB('127.0.0.1', 8888) print(&q ...
- [8.3] Magic Index
A magic index in an array A[0...n-1] is defined to be an index such that A[i] = i. Given a sorted ar ...
- 【BZOJ1911】[Apio2010]特别行动队 斜率优化DP
想了好久啊....——黑字为第一次更新.——这里是第二次更新,维护上下凸包据题而论,第一种方法是化式子的方法,需要好的化式子的方法,第二种是偏向几何,十分好想,纯正的维护凸包的方法,推荐. 用了我感觉 ...
- Android应用-听听
听听是一款记歌词音乐播放器android应用. 功能特点:1.搜索网络歌词.2.本地音乐分类播放.3.离线保存网络歌词.4.页面整洁干净,风格清新. 下载APP 屏幕截图:
- mvn使用笔记
mvn命令格式: You must specify a valid lifecycle phase or a goal in the format <plugin-prefix>:< ...
- gulp-babel使用
各大浏览器厂商对es2015功能支持不完全,等到全部支持会等很长时间,如果现在使用es2015,可以选择babel一个将ES6/ES7写的代码转换为ES5代码的编译器. 我们选择使用gulp自动化编译 ...
- STM32之通用定时器
广大的互联网的大家早上中午晚上..又好..没错了..我又来了..写博客不是定时的..为什么我要提写博客不是定时的呢??聪明的人又猜到我要说什么了吧.有前途.其实我还是第一次听到定时器有通用和高级之分的 ...
- [转]android:动态创建多个按钮 及 批量设置监听
之前投机取巧,先创建好多个按钮,再根据需要的数量进行部分隐藏,不过还是逃不过呀. 这样根本无法批量地 findId,批量地 设置监听. 所以今天还是认认真真地研究回“动态创建按钮”,终于,通过不断尝试 ...
- iOS开发查看手机app本地存储的文件
开发过程中,有时会在本地存储一些文件,但是我们不确定有没有存上,可以通过以下方法来查看测试手机上本地存储的文件: 1.选择xcode上面的window下面的Devices 2.先在左边选中你当前的设备 ...
- mui框架中底部导航的跳转2
接上一篇 还有一种方法就是在一心得页面中打开我们所需要的网页 代码如下: 向新的的页面穿值: 获取到新页面上的值: