文档碎片是什么

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('&nbsp;', $e, '&nbsp;', $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的更多相关文章

  1. 解密jQuery内核 DOM操作的核心函数domManip

    domManip是什么 dom即Dom元素,Manip是Manipulate的缩写,连在一起就是Dom操作的意思. .domManip()是jQuery DOM操作的核心函数 对封装的节点操作做了参数 ...

  2. 解密jQuery内核 DOM操作

    jQuery针对DOM操作的插入的方法有大概10种 append.prepend.before.after.replaceWith appendTo.prependTo.insertBefore.in ...

  3. 解密jQuery内核 DOM操作方法(二)html,text,val

    回顾下几组DOM插入有关的方法 innerHTML 设置或获取位于对象起始和结束标签内的 HTML outerHTML 设置或获取对象及其内容的 HTML 形式 看图对照区别 innerText 设置 ...

  4. 解密jQuery内核 样式操作

    基础回顾 jQuery里节点样式读取以及设置都是通过.css()这个方法来实现的,本章通一下分解探究下jquery里这部分代码的实现 那么jQuery要处理样式的哪些问题? 先简单回顾下样式操作会遇到 ...

  5. jQuery的DOM操作详解

    DOM(Document Object Model-文档对象模型):一种与浏览器, 平台, 语言无关的规则, 使用该接口可以轻松地访问页面中所有的标准组件DOM操作的分类 核心-DOM: DOM Co ...

  6. 第3章 jQuery的DOM操作

    一.  DOM 分为DOM核心,HTML-DOM和CSS-DOM 1.DOM核心 不专属与javascript. 获取对象:document.getElementsByTagName('div') 获 ...

  7. jQuery – 3.JQuery的Dom操作

    3.1 JQuery的Dom操作     1.使用html()方法读取或者设置元素的innerHTML    2.使用text()方法读取或者设置元素的innerText     3.使用attr() ...

  8. js,jQuery和DOM操作的总结(二)

    jQuery的基本操作 (1)遍历键值对和数组 , , , , , ]; $.map(arr, function (ele, index) { alert(ele + '===' + index); ...

  9. 03-老马jQuery教程-DOM操作

    jQuery DOM操作 在没有jQuery之前,DOM的操作相对来说有点麻烦,尤其是DOM节点的搜索.目前我们已经学习了jQuery的选择器,接下带大家一块学习jQuery的DOM操作,jQuery ...

随机推荐

  1. 使用js-xlsx库,前端读取Excel报表文件

    在实际开发中,经常会遇到导入Excel文件的需求,有的产品人想法更多,想要在前端直接判断文件内容格式是否正确,必填项是否已填写 依据HTML5的FileReader,可以使用新的API打开本地文件(参 ...

  2. 51Nod 1010 只包含因子2 3 5的数 Label:None

    K的因子中只包含2 3 5.满足条件的前10个数是:2,3,4,5,6,8,9,10,12,15. 所有这样的K组成了一个序列S,现在给出一个数n,求S中 >= 给定数的最小的数. 例如:n = ...

  3. [8.1] Triple Step

    A child is running up a staircase with n steps and can hop either 1 step, 2 steps, or 3 steps at a t ...

  4. [Protobuf] Mac系统下安装配置及简单使用

    Mac下Protobuf安装 Protobuf源码Github地址: https://github.com/google/protobuf 配置环境教程: https://github.com/goo ...

  5. [RxJava]在学习RxJava中的错误理解

    关于RxJava语法的问题与理解   最近看到一个blog-RxJava 写到关于Observable数据异步加载的介绍.针对fromCallable和just操作符在使用上的问题来跟大家针对代码进行 ...

  6. 正则表达式preg_replace中危险的/e修饰符带来的安全漏洞问题

    mixed preg_replace ( mixed pattern, mixed replacement, mixed subject [, int limit]) /e 修饰符使 preg_rep ...

  7. Centos 7 安装 设置 IP地址,DNS,主机名,防火墙,端口,SELinux (实测+笔记)

    环境: 系统硬件:vmware vsphere (CPU:2*4核,内存2G,双网卡) 系统版本:CentOS-7.0-1406-x86_64-DVD.iso 安装步骤: 1.虚拟系统安装 1.1 使 ...

  8. JS 的事件委托机制

    以前写上图所示的鼠标点击触发事件,一般都是用如下所示的给每一个表示列表的标签绑定一个click事件(演示用的例子的框架是React): 毫无疑问,这样是比较繁琐的,以后维护修改改个函数名什么的还不方便 ...

  9. 回忆那些我们曾今铭记过的.NET重点知识

    正如标题所说的那样,到底是那些.NET的知识点呢?     接下来就让我带着你们去了解这些知识点吧! 1.接口 2.索引器 3.FOREACH的本质 4.匿名内部类 5.运算符的重载 一.什么是接口? ...

  10. Matlab 视频与图像之间的相互转换

    matlab版本是2015b,其中更新了部分函数库之前网上相关程序不适用于新版本,在此提供适用于新版本的程序: 帧序列转换为视频 function video = frames2video(frame ...