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

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

  我们知道在对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操作续之克隆节点的全部叙述,希望大家喜欢。

(0)

相关推荐

  • Jquery1.9.1源码分析系列(六)延时对象应用之jQuery.ready

    还记不记得jQuery初始化函数jQuery.fn.init中有这样是一个分支 //document ready简便写法$(function(){-}) } else if ( jQuery.isFunction( selector ) ) { return rootjQuery.ready( selector ); } 所以$(fn)===$(document).ready(fn). 来看一下jQuery.fn.ready的源码 ready: function( fn ) { // Add t

  • jQuery-1.9.1源码分析系列(十)事件系统之事件体系结构

    又是一个重磅功能点. 在分析源码之前分析一下体系结构,有助于源码理解.实际上在jQuery出现之前,Dean Edwards的跨浏览器AddEvent()设计做的已经比较优秀了:而且jQuery事件系统的设计思想也是基于该思想的,所以我们先分析一下Dean Edwards前辈的事件绑定. a. jQuery事件原型--Dean Edwards的跨浏览器AddEvent()设计 源码解读 //事件添加方法 function addEvent(element, type, handler) { //

  • jQuery 1.9.1源码分析系列(十)事件系统之主动触发事件和模拟冒泡处理

    发现一个小点,先前没有注意的 stopPropagation: function() { var e = this.originalEvent; ... if ( e.stopPropagation ) { e.stopPropagation(); } jQuery重载stopPropagation函数调用的本地事件对象的stopPropagation函数阻止冒泡.也就是说,阻止冒泡的是当前节点,而不是事件源. 说到触发事件,我们第一反应是使用$(...).click()这种方式触发click事

  • jQuery-1.9.1源码分析系列(十)事件系统之事件包装

    在上篇文章给大家介绍了jQuery-1.9.1源码分析系列(十)事件系统之事件体系结构,本篇继续给大家介绍jquery1.9.1源码分析系列相关知识,具体内容请看下文吧. 首先需要明白,浏览器的原生事件是只读的,限制了jQuery对他的操作.举个简单的例子就能明白为什么jQuery非要构造一个新的事件对象. 在委托处理中,a节点委托b节点在a被click的时候执行fn函数.当事件冒泡到b节点,执行fn的时候上下文环境需要保证正确,是a节点执行了fn而非b节点.如何保证执行fn的上下文环境是a节点

  • jQuery 1.9.1源码分析系列(十五)动画处理之缓动动画核心Tween

    在jQuery内部函数Animation中调用到了createTweens()来创建缓动动画组,创建完成后的结果为: 可以看到上面的缓动动画组有四个原子动画组成.每一个原子动画的信息都包含在里面了. 仔细查看createTweens函数,实际上就是遍历调用了tweeners ["*"]的数组中的函数(实际上就只有一个元素). function createTweens( animation, props ) { jQuery.each( props, function( prop, v

  • Jquery-1.9.1源码分析系列(十一)之DOM操作

    DOM操作包括append.prepend.before.after.replaceWith.appendTo.prependTo.insertBefore.insertAfter.replaceAll.其核心处理函数是domManip. DOM操作函数中后五种方法使用的依然是前面五种方法,源码 jQuery.each({ appendTo: "append", prependTo: "prepend", insertBefore: "before&quo

  • jQuery1.9.1针对checkbox的调整方法(prop)

    在jquery 1.8.x中的版本,我们对于checkbox的选中与不选中操作如下: 判断是否选中 复制代码 代码如下: $('#checkbox').prop('checked') 设置选中与不选中状态: 复制代码 代码如下: $('#checkbox').attr('checked',true)$('#checkbox').attr('checked',false) 但此方法在jquery1.9.1中,有些处理不一样 IE10,Chrome,FF中,对于选中状态,第一次$('#checkbo

  • jQuery 1.9.1源码分析系列(十四)之常用jQuery工具

    为了给下一章分析动画处理做准备,先来看一下一些工具.其中队列工具在动画处理中被经常使用. jQuery.fn. queue(([ queueName ] [, newQueue ]) || ([ queueName ,] callback ))(获取或设置当前匹配元素上待执行的函数队列. 如果当前jQuery对象匹配多个元素:获取队列时,只获取第一个匹配元素上的队列:设置队列(替换队列.追加函数)时,则为每个匹配元素都分别进行设置.如果需要移除并执行队列中的第一个函数,请使用dequeue()函

  • jQuery 1.9.1源码分析系列(十三)之位置大小操作

    先给大家展示谢 jQuery.fn.css (propertyName [, value ]| object )(函数用于设置或返回当前jQuery对象所匹配的元素的css样式属性值.如果需要删除指定的css属性,请使用该函数将其值设为空字符串("") 注意:1.如果省略了value参数,则表示获取属性值:如果指定了该参数,则表示设置属性值.2.css()函数的所有"设置"操作针对的是当前jQuery对象所匹配的每一个元素:所有"读取"操作只针对

  • Jquery 1.9.1源码分析系列(十二)之筛选操作

    废话不多说了直接奔入主题了. jQuery.fn.find( selector ) find接受一个参数表达式selector:选择器(字符串).DOM元素(Element).jQuery对象.分两种情况处理: 第一种,如果传入的参数是非字符串,则先通过jQuery选择器将selector查找出来,然后过滤出包含于当前jQuery对象所匹配的元素的节点. if ( typeof selector !== "string" ) { self = this; return this.pus

  • Android Tween动画之RotateAnimation实现图片不停旋转效果实例介绍

    主要介绍Android中如何使用rotate实现图片不停旋转的效果.Android 平台提供了两类动画,一类是 Tween 动画,即通过对场景里的对象不断做图像变换(平移.缩放.旋转)产生动画效果:第二类是 Frame 动画,即顺序播放事先做好的图像,跟电影类似.本文分析 Tween动画的rotate实现旋转效果. 在新浪微博客户端中各个操作进行中时activity的右上角都会有个不停旋转的图标,类似刷新的效果,给用户以操作中的提示.这种非模态的提示方式推荐使用,那么下面就分享下如何实现这种效果

  • jQuery 1.9.1源码分析系列(十)事件系统之绑定事件

    事件绑定的方法有很多种,使用了jquery那么原理那种绑定方式(elem.click = function(){...}))就不太想推荐给大家了.最主要的原因是elem.click=fn这种方式只能绑定一个事件处理,多次绑定的只会保留最后一次绑定的结果. 下面给大家介绍jquery绑定事件的方式有哪些吧. 复制代码 代码如下: jQuery.fn.eventType([[data,] fn]) 比如eventType指的是事件类型,比如click: $("#chua").click(f

随机推荐