jQuery-1.9.1源码分析系列(十一)DOM操作续之克隆节点


Posted in Javascript onDecember 01, 2015

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

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

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

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

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

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/2
  destElements = getAll( clone );
  srcElements = getAll( elem );
  //修正所有IE克隆问题
  for ( i = 0; (node = srcElements[i]) != null; ++i ) {
    // Ensure that the destination node is not null; Fixes #9587
    if ( 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 );
    }
  }

第四步,保护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 #10324
    if ( jQuery.support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) {
      dest.innerHTML = src.innerHTML;
    }
  }

4.IE6-8无法克隆一个复选框或单选按钮的选中状态。需要主动设置。

// manipulation_rcheckableType = /^(?:checkbox|radio)$/i
  else 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操作续之克隆节点的全部叙述,希望大家喜欢。

Javascript 相关文章推荐
jquery实现的让超出显示范围外的导航自动固定屏幕最顶上
Sep 22 Javascript
JavaScript+html5 canvas绘制缤纷多彩的三角形效果完整实例
Jan 26 Javascript
javascript中arguments,callee,caller详解
Mar 16 Javascript
jQuery实现选项联动轮播效果【附实例】
Apr 19 Javascript
BOM系列第三篇之定时器应用(时钟、倒计时、秒表和闹钟)
Aug 17 Javascript
轻松掌握JavaScript状态模式
Sep 07 Javascript
基于jQuery实现瀑布流页面
Apr 11 jQuery
Bootstrap Multiselect 常用组件实现代码
Jul 09 Javascript
vuex中的 mapState,mapGetters,mapActions,mapMutations 的使用
Apr 13 Javascript
JS加密插件CryptoJS实现的Base64加密示例
Aug 16 Javascript
js利用iframe实现选项卡效果
Aug 09 Javascript
微信小程序实现简单购物车功能
Dec 30 Javascript
快速学习jQuery插件 Cookie插件使用方法
Dec 01 #Javascript
快速学习jQuery插件 jquery.validate.js表单验证插件使用方法
Dec 01 #Javascript
JavaScript使用DeviceOne开发实战(二) 生成调试安装包
Dec 01 #Javascript
JavaScript使用DeviceOne开发实战(一) 配置和起步
Dec 01 #Javascript
快速学习jQuery插件 Form表单插件使用方法
Dec 01 #Javascript
jQuery学习笔记之Ajax用法实例详解
Dec 01 #Javascript
易操作的jQuery表单提示插件
Dec 01 #Javascript
You might like
php环境配置之CGI、FastCGI、PHP-CGI、PHP-FPM、Spawn-FCGI比较?
2011/10/17 PHP
PHP自动识别字符集并完成转码详解
2013/08/02 PHP
自写简单JS判断是否已经弹出页面
2010/10/20 Javascript
js正则表达式的使用详解
2013/07/09 Javascript
javascript实现存储hmtl字符串示例
2014/04/25 Javascript
在JS数组特定索引处指定位置插入元素的技巧
2014/08/24 Javascript
Javascript中prototype属性实现给内置对象添加新的方法
2015/05/14 Javascript
jsTree使用记录实例
2016/12/01 Javascript
如何提高数据访问速度
2016/12/26 Javascript
jQuery实现拖拽可编辑模块功能代码
2017/01/12 Javascript
js实现无缝滚动图(可控制当前滚动的方向)
2017/02/22 Javascript
Node.js Express安装与使用教程
2018/05/11 Javascript
详解jQuery如何实现模糊搜索
2019/05/10 jQuery
js中关于Blob对象的介绍与使用
2019/11/29 Javascript
javascript中的相等操作符(==与===区别)
2019/12/21 Javascript
Vue中fragment.js使用方法小结
2020/02/17 Javascript
谈谈JavaScript令人迷惑的==与+
2020/08/31 Javascript
[01:04:14]VP vs TNC 2018国际邀请赛小组赛BO2 第二场 8.17
2018/08/20 DOTA
[00:44]华丽开场!DOTA2勇士令状带来全新对阵画面
2019/05/15 DOTA
Python实现的检测web服务器健康状况的小程序
2014/09/17 Python
在Ubuntu系统下安装使用Python的GUI工具wxPython
2016/02/18 Python
基于Django模板中的数字自增(详解)
2017/09/05 Python
python通过文本在一个图中画多条线的实例
2020/02/21 Python
keras和tensorflow使用fit_generator 批次训练操作
2020/07/03 Python
Matplotlib 绘制饼图解决文字重叠的方法
2020/07/24 Python
python3处理word文档实例分析
2020/12/01 Python
可口可乐唇膏:Lip Smackers
2019/08/27 全球购物
中专毕业个人的自荐信格式
2013/09/21 职场文书
我的中国梦演讲稿800字
2014/08/19 职场文书
机关党员四风问题个人整改措施
2014/10/26 职场文书
2014年高校辅导员工作总结
2014/12/09 职场文书
会议接待欢迎词范文
2015/01/26 职场文书
手把手教你制定暑期学习计划,让你度过充实的暑假
2019/08/22 职场文书
Python 内置函数速查表一览
2021/06/02 Python
python数字类型和占位符详情
2022/03/13 Python
MySQL创建管理子分区
2022/04/13 MySQL