本節說一下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 = https://www.cnblogs.com/greatdesert/p/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 = https://www.cnblogs.com/greatdesert/p/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就是執行到這里的,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/31422.html
標籤:jQuery
上一篇:編輯表格輸入內容、根據input輸入框輸入數字動態生成表格行數、編輯表格內容提交傳給后臺資料處理
下一篇:js獲取串列多條資料(介面)
