前言:这篇讲完后,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…
(完)