什么情况下使用到克隆节点?

  我们知道在对DOM操作过程中如果直接使用节点会出现节点随操作而变动的情况。比如对节点使用.after/.before/.append等方法后,节点被添加到新的地方,原来的位置上的节点被移除了。有的时候需要保留原来位置上的节点,仅仅是需要一个副本添加到对应位置,这个时候克隆就有了使用场景。

  jQuery.fn.clone克隆当前匹配元素集合的一个副本,并以jQuery对象的形式返回。

  你还可以指定是否复制这些匹配元素(甚至它们的子元素)的附加数据( data()函数 )和绑定事件。

  jQueyr.fn.clone: function( withDataAndEvents, deepDataAndEvents )参数描述

withDataAndEvents

可选/Boolean类型,是否同时复制元素的附加数据和绑定事件,默认为false。

deepWithDataAndEvents

可选/Boolean类型,是否同时复制元素的所有子元素的附加数据和绑定事件,默认值即为参数withDataAndEvents的值。

  

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操作续——克隆节点相关推荐

  1. 菜鸟读jQuery 2.0.3 源码分析系列(1)

    原文链接在这里,作为一个菜鸟,我就一边读一边写 jQuery 2.0.3 源码分析系列 前面看着差不多了,看到下面一条(我是真菜鸟),推荐木有入门或者刚刚JS入门摸不着边的看看,大大们手下留情,想一起 ...

  2. jQuery源码分析系列:属性操作

    属性操作 1.6.1相对1.5.x最大的改进,莫过于对属性.attr()的重写了.在1.6.1中,将.attr()一分为二: .attr()..prop(),这是一个令人困惑的变更,也是一个破坏性的升 ...

  3. jQuery源码分析系列

    声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...

  4. [转]jQuery源码分析系列

    文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaro ...

  5. [转载]jQuery1.6.1源码分析系列

    转载:http://www.cnblogs.com/nuysoft/archive/2011/11/14/2248023.html [原创] jQuery1.6.1源码分析系列(停止更新) 作者:nu ...

  6. MyBatis 源码分析系列文章合集

    1.简介 我从七月份开始阅读MyBatis源码,并在随后的40天内陆续更新了7篇文章.起初,我只是打算通过博客的形式进行分享.但在写作的过程中,发现要分析的代码太多,以至于文章篇幅特别大.在这7篇文章 ...

  7. MyBatis 源码分析系列文章导读

    1.本文速览 本篇文章是我为接下来的 MyBatis 源码分析系列文章写的一个导读文章.本篇文章从 MyBatis 是什么(what),为什么要使用(why),以及如何使用(how)等三个角度进行了说 ...

  8. Spring IOC 容器源码分析系列文章导读

    1. 简介 前一段时间,我学习了 Spring IOC 容器方面的源码,并写了数篇文章对此进行讲解.在写完 Spring IOC 容器源码分析系列文章中的最后一篇后,没敢懈怠,趁热打铁,花了3天时间阅 ...

  9. Spring IOC 容器源码分析系列文章导读 1

    1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本.经过十几年的迭代,现在的 Spring 框架已经非常成熟了.Spring ...

  10. dubbo源码分析系列(1)扩展机制的实现

    1 系列目录 dubbo源码分析系列(1)扩展机制的实现 dubbo源码分析系列(2)服务的发布 dubbo源码分析系列(3)服务的引用 dubbo源码分析系列(4)dubbo通信设计 2 SPI扩展 ...

最新文章

  1. 对大龄程序员的五大误解
  2. 「SAP技术」SAP MM 不能向被分配了工厂代码的供应商采购服务?
  3. 盘点那些具有“爆款”潜力的智能家居产品
  4. 前端笔试题(附答案)
  5. 高老师架构设计思考短句集(1)
  6. day27:三剑客之sed
  7. 计算机科学与技术导论%1计算机网络,汕头大学计算机专业课程计划
  8. 全网首发:制作LINUX安装软件包,要处理哪些系统目录和文件(3)
  9. 计算机组成原理试题库10,计算机组成原理试题10
  10. 绝对地址、相对地址、/、./、../之间的区别
  11. error: skipping because parent directory has insecure permissions问题
  12. C02014010宋明妤信息论作业
  13. 关于C++中的随机数生成器
  14. 2023计算机毕业设计SSM最新选题之java住院病人管理系统pebfh
  15. 【大学生辩论赛】如何练习自己的辩论口才
  16. 顶刊TIP 2022!阿里提出:从分布视角出发理解和提升对抗样本的迁移性
  17. Oracle闪回技术详解
  18. (翻译)Decision-Making in Driver-Automation Shared Control
  19. java学习p163
  20. MyBatis官方文档-日志

热门文章

  1. golang 时间戳和时间互转
  2. LINUX编译mate-desktop/pluma-1.26.0文本编辑器
  3. VirtualBox虚拟机硬盘容量扩容
  4. 领导问“BUG解决了没有”,吾一听就知道完蛋了
  5. 软件基本功:测试先从做功能文档开始,不断积累
  6. Perhaps you should add the directory containing libpcre.pc to the PKG_CONFIG_PATH
  7. 主机通过网络访问虚拟机VirtualBox的WEB服务器
  8. 强烈抗议故意审核不通过
  9. JDK 32位(x86)版本,最新的是JDK8
  10. C++中Vector/Map/List中尽量使用指针,避免直接保存对象