经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JS/JS库/框架 » jQuery » 查看文章
jQuery 源码解析(二十二) DOM操作模块 复制元素 详解
来源:cnblogs  作者:大沙漠  时间:2019/11/12 8:44:04  对本文有异议

本节说一下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

还是先举个栗子:

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

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

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

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

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

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

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

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

原文链接:http://www.cnblogs.com/greatdesert/p/11814242.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号