本節說一下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就是執行到這裏的。