前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >jQuery源码解析之clone()

jQuery源码解析之clone()

作者头像
进击的小进进
发布2022-03-28 14:54:55
2.6K0
发布2022-03-28 14:54:55
举报

前言:这篇讲完后,jQuery的文档处理就告一段落了,有空我把这部分整合下,发一篇文章目录。

一、示例代码

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>jQuery源码解析之clone()</title>
</head>
<body>
<script src="jQuery.js"></script>

<div id="divTwo">
  <p id="pTwo">这是divTwo
    <span id="spanTwo">这是spanTwo</span>
  </p>
</div>
<div id="divOne">

</div>

<script>
  $("#pTwo").click(function () {
    alert("pTwo被点击了")
  })
  $("#spanTwo").click(function (e) {
    //阻止冒泡
    e.stopPropagation()
    alert("spanTwo被点击了")
  })
  // $("#pTwo").clone().appendTo($("#divOne"))
  // $("#pTwo").clone(true,false).appendTo($("#divOne"))
  $("#pTwo").clone(true,true).appendTo($("#divOne"))
</script>
</body>
</html>

二、$().clone() 作用: 生成被选元素的副本,包含子节点、文本和属性

注意:('div').clone(true) 表示克隆目标节点的事件和数据('div').clone(true,true) 表示克隆目标节点及其子节点的事件和数据

源码:

代码语言:javascript
复制
jQuery.fn.extend({
    //克隆目标节点及其子节点
    //dataAndEvents是否克隆目标节点的事件和数据,默认是false
    //deepDataAndEvents是否克隆目标节点子节点的事件和数据,默认值是dataAndEvents
    //源码6327行
    clone: function( dataAndEvents, deepDataAndEvents ) {
      //默认是false
      dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
      //默认是dataAndEvents
      deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
      //循环调用jQuery.clone
      return this.map( function() {
        return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
      } );
    },
});

解析: 可以看到,这里还是比较简单的,需要注意的就是参数deepDataAndEvents不填的话,其值是根据参数dataAndEvents的值来定的

三、jQuery.clone() 作用同上

源码:

代码语言:javascript
复制
jQuery.extend( {
    //源码6117行
    //生成被选元素的副本,包含子节点、文本和属性
    clone: function( elem, dataAndEvents, deepDataAndEvents ) {
      var i, l, srcElements, destElements,
        //拷贝目标节点的属性和值
        //如果为true,则包括拷贝子节点的所有属性和值
        clone = elem.cloneNode( true ),
        //判断elem是否脱离文档流
        inPage = jQuery.contains( elem.ownerDocument, elem );

      // Fix IE cloning issues
      //兼容性处理,解决IE bug
      if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) &&
        !jQuery.isXMLDoc( elem ) ) {

        // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2
        destElements = getAll( clone );
        srcElements = getAll( elem );

        for ( i = 0, l = srcElements.length; i < l; i++ ) {
          fixInput( srcElements[ i ], destElements[ i ] );
        }
      }

      // Copy the events from the original to the clone
      //从目标节点克隆事件,并绑定给克隆的元素
      if ( dataAndEvents ) {
        //克隆子节点的事件和数据
        if ( deepDataAndEvents ) {
          //源节点
          srcElements = srcElements || getAll( elem );
          //克隆节点
          destElements = destElements || getAll( clone );

          for ( i = 0, l = srcElements.length; i < l; i++ ) {
            cloneCopyEvent( srcElements[ i ], destElements[ i ] );
          }
        }
        //只克隆目标节点和数据
        else {
          cloneCopyEvent( elem, clone );
        }
      }

      //将script标签设为已运行
      // Preserve script evaluation history
      destElements = getAll( clone, "script" );
      if ( destElements.length > 0 ) {
        setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
      }

      // Return the cloned set
      return clone;
    },
})

解析: 可以看到这部分源码主要分为三大块: (1)解决 IE 的 bug,主要是在fixInput()方法上进行处理 (2)从目标节点克隆数据、添加事件给克隆的元素 (3)将克隆的元素中的script标签设为已运行

四、fixInput() 作用: (1)解决 IE 无法保存克隆的单选、多选的状态的 bug (2)解决 IE 无法将克隆的选项返回至默认选项状态的 bug

源码:

代码语言:javascript
复制
  //解决IE的bug:(1)无法保存克隆的单选、多选的状态 (2)无法将克隆的选项返回至默认选项状态
  // Fix IE bugs, see support tests
  //源码5937行
  function fixInput( src, dest ) {
    var nodeName = dest.nodeName.toLowerCase();

    // Fails to persist the checked state of a cloned checkbox or radio button.
    //IE无法保存克隆的单选框和多选框的选择状态
    if ( nodeName === "input" && rcheckableType.test( src.type ) ) {
      dest.checked = src.checked;
    }
    //IE无法将克隆的选项返回至默认选项状态
    // Fails to return the selected option to the default selected state when cloning options
    else if ( nodeName === "input" || nodeName === "textarea" ) {
      dest.defaultValue = src.defaultValue;
    }
  }

解析: 本质就是将目标元素的checked属性defaultValue属性手动赋值给克隆的元素。

五、cloneCopyEvent() 作用: $().clone()的关键方法,用来从目标节点克隆数据、添加事件给克隆的元素

注意: jQuery 采用数据分离的方法来保存 DOM 上的事件和数据,利用 uuid 标记每个 DOM 元素,然后在内存上,将每个 DOM 元素相关的数据放到内存中,然后在 uuid 和内存的数据之间建立映射。

优点是方便复制数据。

注意:事件是不可赋值的,只能一个个添加!

示意图:

源码:

代码语言:javascript
复制
 //src:目标元素
  //dest:克隆的元素
  //源码5902行
  function cloneCopyEvent( src, dest ) {
    var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events;

    if ( dest.nodeType !== 1 ) {
      return;
    }
    //拷贝jQuery内部数据:事件、处理程序等
    // 1. Copy private data: events, handlers, etc.
    if ( dataPriv.hasData( src ) ) {
      //private data old,即目标元素的数据
      //注意:jQuery是通过uuid将目标元素进行标记,
      //然后将与目标元素相关的数据都放到内存中
      //通过uuid和内存的数据建立映射
      //这种数据分离的做法有利于复制数据,但不能复制事件
      pdataOld = dataPriv.access( src );
      //private data current,即为克隆的元素设置数据
      pdataCur = dataPriv.set( dest, pdataOld );
      events = pdataOld.events;
      //如果事件存在
      if ( events ) {
        //移除克隆对的元素的处理程序和事件
        delete pdataCur.handle;
        pdataCur.events = {};
        //依次为克隆的元素添加事件
        //注意:事件是不能被复制的,所以需要重新绑定
        for ( type in events ) {
          for ( i = 0, l = events[ type ].length; i < l; i++ ) {
            jQuery.event.add( dest, type, events[ type ][ i ] );
          }
        }
      }
    }

    // 2. Copy user data
    //拷贝用户数据
    if ( dataUser.hasData( src ) ) {
      udataOld = dataUser.access( src );
      udataCur = jQuery.extend( {}, udataOld );
      //为克隆的元素设置数据
      dataUser.set( dest, udataCur );
    }
  }

解析: (1)拷贝 jQuery 内部数据(事件、处理程序) 拷贝赋值数据:

代码语言:javascript
复制
pdataOld = dataPriv.access( src );
//private data current,即为克隆的元素设置数据
pdataCur = dataPriv.set( dest, pdataOld );

拷贝添加事件:

代码语言:javascript
复制
jQuery.event.add( dest, type, events[ type ][ i ] );

(2)拷贝用户数据

代码语言:javascript
复制
dataUser.set( dest, udataCur );

六、setGlobalEval() 作用: 设置目标元素内部的<script>标签为已执行

源码:

代码语言:javascript
复制
  //设置目标元素内部的`<script>`标签为已执行
  //源码4934行
  // Mark scripts as having already been evaluated
  function setGlobalEval( elems, refElements ) {
    var i = 0,
      l = elems.length;

    for ( ; i < l; i++ ) {
      dataPriv.set(
        elems[ i ],
        "globalEval",
        !refElements || dataPriv.get( refElements[ i ], "globalEval" )
      );
    }
  }

Github: https://github.com/AttackXiaoJinJin/jQueryExplain

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-04-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 webchen 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档