前言:這篇講完後,jQuery的文檔處理
就告一段落了,有空我把這部分整合下,發一篇文章目錄。javascript
1、示例代碼php
<!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()
做用:
生成被選元素的副本,包含子節點、文本和屬性html
注意:$('div')
.clone(true
) 表示克隆目標節點的事件和數據
$('div')
.clone(true,true
) 表示克隆目標節點及其子節點的事件和數據
java
源碼:node
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
的值來定的git
3、jQuery.clone()
做用同上github
源碼:markdown
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標籤設爲已運行app
4、fixInput()
做用:
(1)解決 IE 沒法保存克隆的單選、多選的狀態的 bug
(2)解決 IE 沒法將克隆的選項返回至默認選項狀態的 bugjsp
源碼:
//解決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:
github.com/AttackXiaoJ…
(完)