jQuery-1.9.1源码分析系列(十一) DOM操作续——克隆节点
什么情况下使用到克隆节点?
我们知道在对DOM操作过程中如果直接使用节点会出现节点随操作而变动的情况。比如对节点使用.after/.before/.append等方法后,节点被添加到新的地方,原来的位置上的节点被移除了。有的时候需要保留原来位置上的节点,仅仅是需要一个副本添加到对应位置,这个时候克隆就有了使用场景。
jQuery.fn.clone克隆当前匹配元素集合的一个副本,并以jQuery对象的形式返回。
你还可以指定是否复制这些匹配元素(甚至它们的子元素)的附加数据( data()函数 )和绑定事件。
jQueyr.fn.clone: function( withDataAndEvents, deepDataAndEvents )参数描述
withDataAndEvents |
可选/Boolean类型,是否同时复制元素的附加数据和绑定事件,默认为false。 |
deepWithDataAndEvents |
可选/Boolean类型,是否同时复制元素的所有子元素的附加数据和绑定事件,默认值即为参数 |
a.克隆函数的底层实现步骤分解如下(jQuery.clone)
第一步,先克隆出DOM节点。对支持正确的节点克隆(即支持elem.cloneNode并保证克隆无误)的DOM节点直接使用cloneNode(true),否则自建一个节点来保存被克隆数据然后获取该节点。
if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) {clone = elem.cloneNode( true );// IE<=8 不能正确克隆已分离、未知的节点 //直接新建一个相同的节点,然后获取 } else { //fragmentDiv是全局变量fragmentDiv.innerHTML = elem.outerHTML;fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); }
第二步,如果是IE浏览器下,则需要通过fixCloneNodeIssues( node, destElements[i] );来逐个修正IE克隆问题。IE克隆解决方案全部包含在了fixCloneNodeIssues中,下一节详细分析。里面的jQuery.support内容点击这里查看更多
//针对ie克隆问题修正 if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) &&(elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {//在这里我们不使用Sizzle的原因是: http://jsperf.com/getall-vs-sizzle/2destElements = getAll( clone );srcElements = getAll( elem );//修正所有IE克隆问题for ( i = 0; (node = srcElements[i]) != null; ++i ) {// Ensure that the destination node is not null; Fixes #9587if ( destElements[i] ) {fixCloneNodeIssues( node, destElements[i] );}} }
第三步,如果要克隆缓存数据(包括普通数据和绑定事件),克隆之。
//克隆绑定的事件 if ( dataAndEvents ) {if ( deepDataAndEvents ) {srcElements = srcElements || getAll( elem );destElements = destElements || getAll( clone );for ( i = 0; (node = srcElements[i]) != null; i++ ) {cloneCopyEvent( node, destElements[i] );}} else {cloneCopyEvent( elem, clone );} }
备注:cloneCopyEvent函数中会将原节点的数据保存到克隆节点中,然后将原节点的事件绑定到新的克隆节点上
function cloneCopyEvent( src, dest ) {if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {return;}var type, i, l,oldData = jQuery._data( src ),curData = jQuery._data( dest, oldData ),//dest是克隆对的节点events = oldData.events;if ( events ) {//保证被克隆的节点的事件对象干净,确保没有后面添加的事件没有重复delete curData.handle;curData.events = {};for ( type in events ) {for ( i = 0, l = events[ type ].length; i < l; i++ ) {jQuery.event.add( dest, type, events[ type ][ i ] );}}}// 使克隆的数据对象化if ( curData.data ) {curData.data = jQuery.extend( {}, curData.data );}}
View Code
第四步,保护script计算历史(全局性地标记scripts代码段已经被执行过了),并回收内存,返回克隆节点。
destElements = getAll( clone, "script" ); if ( destElements.length > 0 ) {setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); }destElements = srcElements = node = null;return clone;
b.IE克隆问题汇总fixCloneNodeIssues(src,dest)
src是原节点,dest是src的克隆节点。
IE克隆问题列一下(IE8+)
1.IE6-8当使用cloneNode会克隆事件(这些事件绑定通过attachEvent)。为保证统一性,需要清除克隆的事件,为后续统一克隆事件做准备
// IE6-8当使用cloneNode复制事件(这些事件绑定通过attachEvent)时进入该分支//清除原来的事件,为克隆事件做准备if ( !jQuery.support.noCloneEvent && dest[ jQuery.expando ] ) {data = jQuery._data( dest );for ( e in data.events ) {jQuery.removeEvent( dest, e, data.handle );}dest.removeAttribute( jQuery.expando );}
2.IE8-克隆脚本标签script的时候克隆的内容结果会是空白。我们需要给他重新赋值,并确保他不会执行脚本内容。
//IE克隆脚本时内容为空白,并试图执行新设置的文本if ( nodeName === "script" && dest.text !== src.text ) {disableScript( dest ).text = src.text;restoreScript( dest );}
3.IE6-10不能克隆使用的classid获取的对象元素的子节点。IE10下,如果父节点为null,则会抛出NoModificationAllowedError异常。需要使用原节点的outerHTML和innerHTML重新赋值。
//IE6-10不能克隆使用的classid获取的对象元素的子节点。//IE10下,如果父节点为null,则会抛出NoModificationAllowedError异常else if ( nodeName === "object" ) {if ( dest.parentNode ) {dest.outerHTML = src.outerHTML;}//对于IE9,这个条分支不可避免。//IE9中克隆对象元素,上述outerHTML策略是不充分的。//如果src具有的innerHTML并且克隆节点却没有,//复制src.innerHTML到dest.innerHTML #10324if ( jQuery.support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) {dest.innerHTML = src.innerHTML;}}
4.IE6-8无法克隆一个复选框或单选按钮的选中状态。需要主动设置。
// manipulation_rcheckableType = /^(?:checkbox|radio)$/ielse if ( nodeName === "input" && manipulation_rcheckableType.test( src.type ) ) {//IE6-8无法坚持一个克隆的复选框或单选按钮的选中状态//更糟的是,如果defaultChecked值没有设置,则IE6-7无法给克隆元素选中状态的外观dest.defaultChecked = dest.checked = src.checked;...}
5.当克隆select标签时,IE6-8无法正确返回select默认选中状态。需要主动设置。
//当克隆选项时,IE6-8无法正确返回select默认选中状态else if ( nodeName === "option" ) {dest.defaultSelected = dest.selected = src.defaultSelected;}
6.当克隆其他类型的input和textare标签时,IE6-8不能正确设置defaultValue为正确的值。需要主动设置。
//当克隆其他类型的input标签时,IE6-8不能正确设置defaultValue为正确的值else if ( nodeName === "input" || nodeName === "textarea" ) {dest.defaultValue = src.defaultValue;}
里面用到disableScript这个函数。函数目的是改变script的type,从而保证在给script赋值后不会被作为脚本执行。这个方式我们可以借鉴
//为安全DOM操作替换/保存script节点元素type属性 function disableScript( elem ) {var attr = elem.getAttributeNode("type");elem.type = ( attr && attr.specified ) + "/" + elem.type;return elem; }
如果觉得本文不错,请点击右下方【推荐】!
jQuery-1.9.1源码分析系列(十一) DOM操作续——克隆节点相关推荐
- 菜鸟读jQuery 2.0.3 源码分析系列(1)
原文链接在这里,作为一个菜鸟,我就一边读一边写 jQuery 2.0.3 源码分析系列 前面看着差不多了,看到下面一条(我是真菜鸟),推荐木有入门或者刚刚JS入门摸不着边的看看,大大们手下留情,想一起 ...
- jQuery源码分析系列:属性操作
属性操作 1.6.1相对1.5.x最大的改进,莫过于对属性.attr()的重写了.在1.6.1中,将.attr()一分为二: .attr()..prop(),这是一个令人困惑的变更,也是一个破坏性的升 ...
- jQuery源码分析系列
声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...
- [转]jQuery源码分析系列
文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaro ...
- [转载]jQuery1.6.1源码分析系列
转载:http://www.cnblogs.com/nuysoft/archive/2011/11/14/2248023.html [原创] jQuery1.6.1源码分析系列(停止更新) 作者:nu ...
- MyBatis 源码分析系列文章合集
1.简介 我从七月份开始阅读MyBatis源码,并在随后的40天内陆续更新了7篇文章.起初,我只是打算通过博客的形式进行分享.但在写作的过程中,发现要分析的代码太多,以至于文章篇幅特别大.在这7篇文章 ...
- MyBatis 源码分析系列文章导读
1.本文速览 本篇文章是我为接下来的 MyBatis 源码分析系列文章写的一个导读文章.本篇文章从 MyBatis 是什么(what),为什么要使用(why),以及如何使用(how)等三个角度进行了说 ...
- Spring IOC 容器源码分析系列文章导读
1. 简介 前一段时间,我学习了 Spring IOC 容器方面的源码,并写了数篇文章对此进行讲解.在写完 Spring IOC 容器源码分析系列文章中的最后一篇后,没敢懈怠,趁热打铁,花了3天时间阅 ...
- Spring IOC 容器源码分析系列文章导读 1
1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本.经过十几年的迭代,现在的 Spring 框架已经非常成熟了.Spring ...
- dubbo源码分析系列(1)扩展机制的实现
1 系列目录 dubbo源码分析系列(1)扩展机制的实现 dubbo源码分析系列(2)服务的发布 dubbo源码分析系列(3)服务的引用 dubbo源码分析系列(4)dubbo通信设计 2 SPI扩展 ...
最新文章
- 对大龄程序员的五大误解
- 「SAP技术」SAP MM 不能向被分配了工厂代码的供应商采购服务?
- 盘点那些具有“爆款”潜力的智能家居产品
- 前端笔试题(附答案)
- 高老师架构设计思考短句集(1)
- day27:三剑客之sed
- 计算机科学与技术导论%1计算机网络,汕头大学计算机专业课程计划
- 全网首发:制作LINUX安装软件包,要处理哪些系统目录和文件(3)
- 计算机组成原理试题库10,计算机组成原理试题10
- 绝对地址、相对地址、/、./、../之间的区别
- error: skipping because parent directory has insecure permissions问题
- C02014010宋明妤信息论作业
- 关于C++中的随机数生成器
- 2023计算机毕业设计SSM最新选题之java住院病人管理系统pebfh
- 【大学生辩论赛】如何练习自己的辩论口才
- 顶刊TIP 2022!阿里提出:从分布视角出发理解和提升对抗样本的迁移性
- Oracle闪回技术详解
- (翻译)Decision-Making in Driver-Automation Shared Control
- java学习p163
- MyBatis官方文档-日志
热门文章
- golang 时间戳和时间互转
- LINUX编译mate-desktop/pluma-1.26.0文本编辑器
- VirtualBox虚拟机硬盘容量扩容
- 领导问“BUG解决了没有”,吾一听就知道完蛋了
- 软件基本功:测试先从做功能文档开始,不断积累
- Perhaps you should add the directory containing libpcre.pc to the PKG_CONFIG_PATH
- 主机通过网络访问虚拟机VirtualBox的WEB服务器
- 强烈抗议故意审核不通过
- JDK 32位(x86)版本,最新的是JDK8
- C++中Vector/Map/List中尽量使用指针,避免直接保存对象