本节说一下DOM操作模块里的复制元素子模块,该模块可以复制一个DOM节点,并且可选择的设置是否复制其数据缓存对象(包含事件信息)和是否深度复制(子孙节点等),API如下:

  • $.clone(elem, dataAndEvents, deepDataAndEvents)    ;jQuery底层方法,返回DOM引用    ;elem是要复制的DOM元素,dataAndEvents和deepDataAndEvents分别表示是否复制克隆元素的数据和事件 和 是否复制深度复制数据和事件
  • $.fn.clone(dataAndEvents,deepDataAndEvents)        ;jQuery实例方法,返回jQuery对象    ;参数同上,如果指定了参数1,参数2为空时,则参数2等于参数1

writer by:大沙漠 QQ:22969969

还是先举个栗子:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="http://libs.baidu.com/jquery/1.7.1/jquery.min.js"></script>
</head>
<body>
<div style="width: 150px;height: 50px;background: #cfc;">
<p>今天天气很好</p>
</div>
<button id="b1">按钮1</button>
<button id="b2">按钮2</button>
<button id="b3">按钮3</button>
<button id="b4">按钮4</button> <script>
$('div').click(function(){console.log('div click');}) //给div绑定一个click事件
$('p').click(function(){console.log('p click');}) //给p绑定一个click事件 /*点击按钮1、按钮2、按钮3、按钮4都将会复制div,并添加到body的末尾,再点击复制出来的区块里的p元素时输出如下:*/
$('#b1').click(function(){$('div').clone().appendTo('body');}) //没有任何输出
$('#b2').click(function(){$('div').clone(true,false).appendTo('body');}) //输出:clone_d1 click
$('#b3').click(function(){$('div').clone(true,true).appendTo('body');}) //输出:clone_p1 click、clone_d1 click
$('#b4').click(function(){$('div').clone(true).appendTo('body');}) //输出:clone_p1 click、clone_d1 click ;因为参数2省略,默认等于参数1
</script>
</body>
</html>

渲染如下:

我们在div和p上分别绑定了两个事件,点击p元素(今天天气很好这个文本)控制台输出如下:

另外我们点击任意一个按钮都会克隆div元素,渲染如下:

只不过点击克隆出来的元素的p元素,也就是箭头点击的文字,控制台输出的内容不同,对于四个按钮分别如下:

  • 按钮1 ;无输出                                 ;clone()未传入参数,因此不会复制数据缓存对象
  • 按钮2 ;输出:一行:div click               ;clone()传入了true和false,因此只会对div的数据缓存对象做一次拷贝,对于p就不会拷贝了
  • 按钮3 ;输出两行:p click和div click  ;clone()传入了两个true,会进行深层次的拷贝,子孙节点的数据对象都会拷贝过来
  • 按钮4  ;输出两行:p click和div click ;clone()只传入一个true,这和按钮3是一样的,且看下面代码分析

源码分析


先介绍一下$.clone(),也就是jQuery的底层方法,如下:

jQuery.extend({
clone: function( elem, dataAndEvents, deepDataAndEvents ) { //复制DOM元素,并修正不兼容属性。dataAndEvents:是否复制数据和事件。deepDataAndEvents:是否复制后代元素的数据和事件。
var srcElements,
destElements,
i,
// IE<=8 does not properly clone detached, unknown element nodes
clone = jQuery.support.html5Clone || !rnoshimcache.test( "<" + elem.nodeName ) ? //如果浏览器支持HTML5元素,或者不支持html5且原始元素不含有html5元素
elem.cloneNode( true ) : //调用原生方法.clone(deep)复制DOM元素
shimCloneNode( elem ); //否则调用函数shimCloneNode()通过"安全文档片段"复制HTML5元素 if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) && //修正不兼容性
(elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {
// IE copies events bound via attachEvent when using cloneNode.
// Calling detachEvent on the clone will also remove the events
// from the original. In order to get around this, we use some
// proprietary methods to clear the events. Thanks to MooTools
// guys for this hotness. cloneFixAttributes( elem, clone ); // Using Sizzle here is crazy slow, so we use getElementsByTagName instead
srcElements = getAll( elem );
destElements = getAll( clone ); // Weird iteration because IE will replace the length property
// with an element if you are cloning the body and one of the
// elements on the page has a name or id of "length"
for ( i = 0; srcElements[i]; ++i ) {
// Ensure that the destination node is not null; Fixes #9587
if ( destElements[i] ) {
cloneFixAttributes( srcElements[i], destElements[i] );
}
}
} // Copy the events from the original to the clone
if ( dataAndEvents ) { //如果复制数据和事件
cloneCopyEvent( elem, clone ); //调用cloneCopyEvent()数据 if ( deepDataAndEvents ) { //如果是深度复制
srcElements = getAll( elem ); //获取源DOM节点所有子节点的DOM引用
destElements = getAll( clone ); //获取目标DOM节点所有子节点的DOM引用 for ( i = 0; srcElements[i]; ++i ) { //遍历源DOM节点的子节点
cloneCopyEvent( srcElements[i], destElements[i] ); //逐个复制数据缓存
}
}
} srcElements = destElements = null; // Return the cloned set
return clone; //最后返回clone,也就是复制出来的DOM元素
},
})

$.clone()调用原生的cloneNode()方法拷贝了一份DOM,对于数据缓存则用cloneCopyEvent()进行拷贝,该函数实现如下:

function cloneCopyEvent( src, dest ) {      //复制数据和事件   ;$.clone()函数调用  ;src是源DOM节点,dest是目标节点

    if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {  //如果dest不是元素节点或 src不含有数据,则直接返回
return;
} var type, i, l,
oldData = jQuery._data( src ), //获取原对象内部数据
curData = jQuery._data( dest, oldData ), //设置目标对象的内部数据 ;这时src和dest两个对象内的事件对象(events和handle属性)都是指向同一个的
events = oldData.events; //原对象的事件对象 if ( events ) { //如果源DOM对象有绑定事件处理函数,则删除目标DOM对象的事件信息,再重新绑定
delete curData.handle; //删除目标对象的监听句柄
curData.events = {}; //重置目标对象的事件列表 for ( type in events ) { //遍历源DOM对象的事件列表
for ( i = 0, l = events[ type ].length; i < l; i++ ) { //遍历绑定的每个函数
jQuery.event.add( dest, type + ( events[ type ][ i ].namespace ? "." : "" ) + events[ type ][ i ].namespace, events[ type ][ i ], events[ type ][ i ].data ); //依次在目标DOM对象上进行绑定事件操作
}
}
} // make the cloned public data object a copy from the original
if ( curData.data ) { //如果源DOM对象含有自定义事件对象
curData.data = jQuery.extend( {}, curData.data ); //也单独拷贝一份,再保存到curData.data中
}
}

在源码里可以看到,如果有绑定了事件对象则会调用jQuery.event.add()依次进行绑定,实现就是这样子的。

对于实例方法$.fn.clone()来说,它的实现如下:

jQuery.fn.extend({
clone: function( dataAndEvents, deepDataAndEvents ) { //创建匹配元素集合的深度复制副本。dataAndEvents:可选的布尔值,表示是否复制数据和事件,默认为false。deepDataAndEvents:可选的布尔值,表示是否深度复制数据和事件,默认为false。
dataAndEvents = dataAndEvents == null ? false : dataAndEvents; //修正dataAndEvents参数
deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; //修正deepDataAndEvents参数 return this.map( function () { //调用发那个发.map()遍历匹配元素集合,在回调函数函数中调用jQuery.clone()复制每个匹配元素。
return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); //调用底层的jQuery.clone()方法
});
},
})

这里可以看到,对于$.fn.clone()来说,如果参数2没有传递,则会修正未参数1,上面例子的按钮4就是执行到这里的。

jQuery 源码解析(二十二) DOM操作模块 复制元素 详解的更多相关文章

  1. jQuery 源码解析(二十四) DOM操作模块 包裹元素 详解

    本节说一下DOM操作模块里的包裹元素子模块,该模块可将当前匹配的元素替换指定的DOM元素,有如下方法: wrap(html)               ;在每个匹配元素的外层添加一层DOM元素   ...

  2. jQuery 源码解析(二十三) DOM操作模块 替换元素 详解

    本节说一下DOM操作模块里的替换元素模块,该模块可将当前匹配的元素替换指定的DOM元素,有两个方法,如下: replaceWith(value)     ;使用提供的新内容来替换匹配元素集合中的每个元 ...

  3. jQuery 源码分析(二十) DOM操作模块 插入元素 详解

    jQuery的DOM操作模块封装了DOM模型的insertBefore().appendChild().removeChild().cloneNode().replaceChild()等原生方法.分为 ...

  4. jQuery 源码分析(二十一) DOM操作模块 删除元素 详解

    本节说一下DOM操作模块里的删除元素模块,该模块用于删除DOM里的某个节点,也可以理解为将该节点从DOM树中卸载掉,如果该节点有绑定事件,我们可以选择保留或删除这些事件,删除元素的接口有如下三个: e ...

  5. jquery源码解析:proxy,access,swap,isArraylike详解

    jQuery的工具方法,其实就是静态方法,源码里面就是通过extend方法,把这些工具方法添加给jQuery构造函数的. jQuery.extend({ ...... guid: 1, //唯一标识符 ...

  6. jquery源码解析:attr,prop,attrHooks,propHooks详解

    我们先来看一下jQuery中有多少个方法是用来操作元素属性的. 首先,看一下实例方法: 然后,看下静态方法(工具方法): 静态方法是内部使用的,我们外面使用的很少,实例方法才是对外的. 接下来,我们来 ...

  7. jquery源码解析:pushStack,end,ready,eq详解

    上一篇主要讲解了jQuery原型中最重要的方法init.接下来再讲一些比较常用的原型方法和属性 core_slice = [].slice, jQuery.fn = jQuery.prototype ...

  8. jQuery 源码解析(二十五) DOM操作模块 html和text方法的区别

    html和text都可以获取和修改DOM节点里的内容,方法如下: html(value)     ;获取匹配元素集合中的一个元素的innerHTML内容,或者设置每个元素的innerHTML内容,   ...

  9. MyBatis源码解析(十二)——binding绑定模块之MapperRegisty

    原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6758456.html 1.回顾 之前解析了解析模块parsing,其实所谓的解析模块就是为 ...

随机推荐

  1. 【Java Web开发学习】Spring4条件化的bean

    [Java Web开发学习]Spring4条件化的bean 转载:https://www.cnblogs.com/yangchongxing/p/9071960.html Spring4引入了@Con ...

  2. 安装mysql5.7(亲手实操)

    https://files-cdn.cnblogs.com/files/yucoder/%E5%AE%89%E8%A3%85mysql5.7.pdf

  3. 2.java三大特性

    1. 封装 方法:将属性值修饰为私有,提供get和set方法.造成所有对对象的访问都是通过方法的调用来完成(配合this的使用) 结果:用户不能直接随意改变一个对象内的属性,必须通过调用方法(验证)来 ...

  4. Leetcode 42 接雨水 双指针

    地址 https://leetcode-cn.com/problems/trapping-rain-water/ 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能 ...

  5. Openstack简述

    1.Openstack项目发展概况: Nova 计算服务 Swift 对象存储服务 Glance   镜像服务 Neturon 网络服务 Keystone   身份认证服务 Celimeter   计 ...

  6. 【CV现状-3.2】纹理与材质

    #磨染的初心--计算机视觉的现状 [这一系列文章是关于计算机视觉的反思,希望能引起一些人的共鸣.可以随意传播,随意喷.所涉及的内容过多,将按如下内容划分章节.已经完成的会逐渐加上链接.] 缘起 三维感 ...

  7. Python面向对象-定制方法

    Python中的class可以定义许多定制方法,可以让我们方便的生成特定的类. 我们之前介绍了__slots__.__len__(),python中还有许多这样的特殊函数: __str__ >& ...

  8. 浅析椭圆曲线加密算法(ECC)

    本文首发于先知社区,原文链接:https://xz.aliyun.com/t/6295 数学基础 黎曼几何中的"平行线" 欧几里得<几何原本>中提出五条公设: 过相异两 ...

  9. Linux是如何启动的?

    参考资料: An introduction to the Linux boot and startup processes 这篇随笔,可以理解为是对这篇英文文章的翻译与个人理解.笔记的整合. 扩展阅读 ...

  10. DiskCatalogMaker for Mac常见问题解答

    DiskCatalogMaker for Mac是Mac上简单实用的磁盘管理工具,可以帮助您对多张光盘使用批量扫描模式, 生成缩略图图像选项,更加清晰,并请将其快速编目引擎与其他编目人员比较,在本篇文 ...