本节说一下DOM操做模块里的复制元素子模块,该模块能够复制一个DOM节点,而且可选择的设置是否复制其数据缓存对象(包含事件信息)和是否深度复制(子孙节点等),API以下:html
writer by:大沙漠 QQ:22969969html5
仍是先举个栗子:node
<!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事件 /*点击按钮一、按钮二、按钮三、按钮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>
渲染以下:jquery
咱们在div和p上分别绑定了两个事件,点击p元素(今每天气很好这个文本)控制台输出以下:浏览器
另外咱们点击任意一个按钮都会克隆div元素,渲染以下:缓存
只不过点击克隆出来的元素的p元素,也就是箭头点击的文字,控制台输出的内容不一样,对于四个按钮分别以下:安全
源码分析app
先介绍一下$.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就是执行到这里的。