前言:這篇講完後,jQuery的文檔處理
就告一段落了,有空我把這部分整合下,發一篇文章目錄。html
1、示例代碼node
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>jQuery源碼解析之clone()</title> </head> <body> <script src="jQuery.js"></script> <div id="divTwo"> <p id="pTwo">這是divTwo <span id="spanTwo">這是spanTwo</span> </p> </div> <div id="divOne"> </div> <script> $("#pTwo").click(function () { alert("pTwo被點擊了") }) $("#spanTwo").click(function (e) { //阻止冒泡 e.stopPropagation() alert("spanTwo被點擊了") }) // $("#pTwo").clone().appendTo($("#divOne")) // $("#pTwo").clone(true,false).appendTo($("#divOne")) $("#pTwo").clone(true,true).appendTo($("#divOne")) </script> </body> </html>
2、$()
.clone()
做用:
生成被選元素的副本,包含子節點、文本和屬性git
注意:$('div')
.clone(true
) 表示克隆目標節點的事件和數據
$('div')
.clone(true,true
) 表示克隆目標節點及其子節點的事件和數據
github
源碼:app
jQuery.fn.extend({ //克隆目標節點及其子節點 //dataAndEvents是否克隆目標節點的事件和數據,默認是false //deepDataAndEvents是否克隆目標節點子節點的事件和數據,默認值是dataAndEvents //源碼6327行 clone: function( dataAndEvents, deepDataAndEvents ) { //默認是false dataAndEvents = dataAndEvents == null ? false : dataAndEvents; //默認是dataAndEvents deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; //循環調用jQuery.clone return this.map( function() { return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); } ); }, });
解析:
能夠看到,這裏仍是比較簡單的,須要注意的就是參數deepDataAndEvents
不填的話,其值是根據參數dataAndEvents
的值來定的jsp
3、jQuery.clone()
做用同上ui
源碼:this
jQuery.extend( { //源碼6117行 //生成被選元素的副本,包含子節點、文本和屬性 clone: function( elem, dataAndEvents, deepDataAndEvents ) { var i, l, srcElements, destElements, //拷貝目標節點的屬性和值 //若是爲true,則包括拷貝子節點的全部屬性和值 clone = elem.cloneNode( true ), //判斷elem是否脫離文檔流 inPage = jQuery.contains( elem.ownerDocument, elem ); // Fix IE cloning issues //兼容性處理,解決IE bug if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && !jQuery.isXMLDoc( elem ) ) { // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 destElements = getAll( clone ); srcElements = getAll( elem ); for ( i = 0, l = srcElements.length; i < l; i++ ) { fixInput( srcElements[ i ], destElements[ i ] ); } } // Copy the events from the original to the clone //從目標節點克隆事件,並綁定給克隆的元素 if ( dataAndEvents ) { //克隆子節點的事件和數據 if ( deepDataAndEvents ) { //源節點 srcElements = srcElements || getAll( elem ); //克隆節點 destElements = destElements || getAll( clone ); for ( i = 0, l = srcElements.length; i < l; i++ ) { cloneCopyEvent( srcElements[ i ], destElements[ i ] ); } } //只克隆目標節點和數據 else { cloneCopyEvent( elem, clone ); } } //將script標籤設爲已運行 // Preserve script evaluation history destElements = getAll( clone, "script" ); if ( destElements.length > 0 ) { setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); } // Return the cloned set return clone; }, })
解析:
能夠看到這部分源碼主要分爲三大塊:
(1)解決 IE 的 bug,主要是在fixInput()
方法上進行處理
(2)從目標節點克隆數據、添加事件給克隆的元素
(3)將克隆的元素中的script標籤設爲已運行lua
4、fixInput()
做用:
(1)解決 IE 沒法保存克隆的單選、多選的狀態的 bug
(2)解決 IE 沒法將克隆的選項返回至默認選項狀態的 bugspa
源碼:
//解決IE的bug:(1)沒法保存克隆的單選、多選的狀態 (2)沒法將克隆的選項返回至默認選項狀態 // Fix IE bugs, see support tests //源碼5937行 function fixInput( src, dest ) { var nodeName = dest.nodeName.toLowerCase(); // Fails to persist the checked state of a cloned checkbox or radio button. //IE沒法保存克隆的單選框和多選框的選擇狀態 if ( nodeName === "input" && rcheckableType.test( src.type ) ) { dest.checked = src.checked; } //IE沒法將克隆的選項返回至默認選項狀態 // Fails to return the selected option to the default selected state when cloning options else if ( nodeName === "input" || nodeName === "textarea" ) { dest.defaultValue = src.defaultValue; } }
解析:
本質就是將目標元素的checked屬性
和defaultValue屬性
手動賦值給克隆的元素。
5、cloneCopyEvent()
做用:$().clone()
的關鍵方法,用來從目標節點克隆數據、添加事件給克隆的元素
注意:
jQuery 採用數據分離的方法來保存 DOM 上的事件和數據,利用 uuid 標記每一個 DOM 元素,而後在內存上,將每一個 DOM 元素相關的數據放到內存中,而後在 uuid 和內存的數據之間創建映射。
優勢是方便複製數據。
注意:事件是不可賦值的,只能一個個添加!
示意圖:
源碼:
//src:目標元素 //dest:克隆的元素 //源碼5902行 function cloneCopyEvent( src, dest ) { var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; if ( dest.nodeType !== 1 ) { return; } //拷貝jQuery內部數據:事件、處理程序等 // 1. Copy private data: events, handlers, etc. if ( dataPriv.hasData( src ) ) { //private data old,即目標元素的數據 //注意:jQuery是經過uuid將目標元素進行標記, //而後將與目標元素相關的數據都放到內存中 //經過uuid和內存的數據創建映射 //這種數據分離的作法有利於複製數據,但不能複製事件 pdataOld = dataPriv.access( src ); //private data current,即爲克隆的元素設置數據 pdataCur = dataPriv.set( dest, pdataOld ); events = pdataOld.events; //若是事件存在 if ( events ) { //移除克隆對的元素的處理程序和事件 delete pdataCur.handle; pdataCur.events = {}; //依次爲克隆的元素添加事件 //注意:事件是不能被複制的,因此須要從新綁定 for ( type in events ) { for ( i = 0, l = events[ type ].length; i < l; i++ ) { jQuery.event.add( dest, type, events[ type ][ i ] ); } } } } // 2. Copy user data //拷貝用戶數據 if ( dataUser.hasData( src ) ) { udataOld = dataUser.access( src ); udataCur = jQuery.extend( {}, udataOld ); //爲克隆的元素設置數據 dataUser.set( dest, udataCur ); } }
解析:
(1)拷貝 jQuery 內部數據(事件、處理程序)
拷貝賦值數據:
pdataOld = dataPriv.access( src ); //private data current,即爲克隆的元素設置數據 pdataCur = dataPriv.set( dest, pdataOld );
拷貝添加事件:
jQuery.event.add( dest, type, events[ type ][ i ] );
(2)拷貝用戶數據
dataUser.set( dest, udataCur );
6、setGlobalEval()
做用:
設置目標元素內部的<script>
標籤爲已執行
源碼:
//設置目標元素內部的`<script>`標籤爲已執行 //源碼4934行 // Mark scripts as having already been evaluated function setGlobalEval( elems, refElements ) { var i = 0, l = elems.length; for ( ; i < l; i++ ) { dataPriv.set( elems[ i ], "globalEval", !refElements || dataPriv.get( refElements[ i ], "globalEval" ) ); } }
Github:
https://github.com/AttackXiaoJinJin/jQueryExplain
(完)