-
Notifications
You must be signed in to change notification settings - Fork 4
/
透过$().append看domMainp和buildFragment.html
228 lines (204 loc) · 8.76 KB
/
透过$().append看domMainp和buildFragment.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>jQuery的遍历结构设计之节点操作</title>
</head>
<body>
<script src="jQuery.js"></script>
<button id="test1">append操作</button>
<table class="inner">
<!--<tbody></tbody>-->
</table>
<script>
//匹配div不支持的标签,如 tr、td等
let rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]+)/i );
//================================
let wrapMap = {
// Support: IE <=9 only
option: [ 1, "<select multiple='multiple'>", "</select>" ],
// XHTML parsers do not magically insert elements in the
// same way that tag soup parsers do. So we cannot shorten
// this by omitting <tbody> or other required elements.
thead: [ 1, "<table>", "</table>" ],
col: [ 2, "<table><colgroup>", "</colgroup></table>" ],
tr: [ 2, "<table><tbody>", "</tbody></table>" ],
td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
_default: [ 0, "", "" ]
};
// Support: IE <=9 only
wrapMap.optgroup = wrapMap.option;
wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
wrapMap.th = wrapMap.td;
//================================
//源码5597行-5586行
//作用是将传入的参数(dom节点元素、字符串、函数)统一转化为符合要求的DOM节点
//例:$('.inner').append('<tr><td>test1</tr></td>')
//nodelist(collections)即$('.inner')
//args即<tr><td>test1</td></tr>
function domManip( nodelist, args, callback ) {
console.log(nodelist,args,'ignored5798')
//数组深复制成新的数组副本
//源码是:args = concat.apply( [], args ),这里没有用arguments,而是传参就改了
let argsArr = []
argsArr.push(args)
console.log(argsArr,'args31')
//l 长度,比如类名为.inner的li有两组,2
let fragment,
first,
node,
i = 0,
//l 长度,比如类名为.inner的li有两组,2
l = nodelist.length,
iNoClone = l - 1
//l=2
console.log(l,'lll45')
if ( l ) {
console.log(argsArr,nodelist[0].ownerDocument,nodelist,'firstChild40')
//argsArr:<p>Test</p>
//nodelist[0].ownerDocument:目标节点所属的文档
fragment = buildFragment(argsArr,nodelist[0].ownerDocument,false,nodelist );
first=fragment.firstChild
console.log(fragment.childNodes,'firstChild42')
//即<p>Test</p>
if (first) {
//=====根据nodelist的长度循环操作========
for ( ; i < l; i++ ) {
console.log(node,fragment.childNodes,'childNodes49')
node = fragment;
if ( i !== iNoClone ) {
/*createDocumentFragment创建的元素是一次性的,添加之后再就不能操作了,
所以需要克隆iNoClone的多个节点*/
node = jQuery.clone( node, true, true );
}
console.log(nodelist[i], node.childNodes,'node50')
//call(this,param)
callback.call( nodelist[i], node);
}
//====================
}
}
console.log(nodelist,'nodelist58')
return nodelist
}
//源码5724行-5733行
//额外判断,当选择器是table,并且插入的元素是tr时,会查找到table下的tbody,并返回tbody
//this, node
function manipulationTarget( selector, node ) {
console.log(node.childNodes,node.firstChild,'node73')
// 如果是table里面插入行tr
if ( nodeName( selector, "table" ) &&
nodeName( node.nodeType !== 11 ? node : node.firstChild, "tr" ) ) {
return jQuery( selector ).children( "tbody" )[ 0 ] || selector
}
return selector
}
//源码2843行-2847行
//判断两个参数的nodename是否相等
function nodeName( selector, name ) {
return selector.nodeName && selector.nodeName.toLowerCase() === name.toLowerCase();
}
//源码4857行-4945行
/*创建文档碎片,原因是一般情况下,我们向DOM中添加新的元素或者节点,DOM会立刻更新。
如果向DOM添加100个节点,那么就得更新100次,非常浪费浏览器资源。
解决办法就是:我们可以创建一个文档碎片(documentFragment),
documentFragment类似于一个小的DOM,在它上面使用innerHTML并在innerHTML上插入多个节点,速度要快于DOM(2-10倍),
比如:先将新添加的100个节点添加到文档碎片的innerHTML上,再将文档碎片添加到DOM上。*/
//args, collection[ 0 ].ownerDocument, false, collection
function buildFragment( arr, context, truefalse, selection ) {
let elem,tmp, nodes = [], i = 0, l = arr.length,wrap,tag,j
// createdocumentfragment()方法创建了一虚拟的节点对象,节点对象包含所有属性和方法。
//相当于document.createDocumentFragment()
let fragment = context.createDocumentFragment()
//l=1
console.log(l,'l87')
//==============
for ( ; i < l; i++ ) {
//'<tr><td></td></tr>'
elem = arr[ i ];
console.log(i,elem,'elem90')
if ( elem || elem === 0 ) {
/*创建div是为了处理innerHTML的缺陷(IE会忽略开头的无作用域元素),
让所有的元素都被div元素给包含起来,包括script,style等无作用域的元素*/
tmp=fragment.appendChild( context.createElement( "div" ) )
//就是匹配div不支持的标签,如 tr、td等
/*不支持innerHTML属性的元素,通过正则单独取出处理*/
tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
/*作用就是利用wrapMap让不支持innerHTML的元素通过包装wrap来支持innerHTML*/
//ie对字符串进行trimLeft操作,其余是用户输入处理
//很多标签不能单独作为DIV的子元素
/*td,th,tr,tfoot,tbody等等,需要加头尾*/
wrap = wrapMap[ tag ] || wrapMap._default // tr: [ 2, "<table><tbody>", "</tbody></table>" ]
console.log(wrap,'wrap152')
//将修正好的element添加进innerHTML中
//jQuery.htmlPrefilter:标签转换为闭合标签,如<table> --> <table></table>
/*div不支持tr、td所以需要添加头尾标签,如<div><table><tbody>xxxx</tbody></table>*/
tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ];
// 因为warp被包装过,需要找到正确的元素父级
j = wrap[ 0 ]; //2
while ( j-- ) {
tmp = tmp.lastChild;
}
//temp:<tbody></tbody>
//tmp.childNodes:tr
//nodes:[]
//jQuery.merge:将两个数组合并到第一个数组中
jQuery.merge( nodes, tmp.childNodes );
}
}
//================
// Remove wrapper from fragment
fragment.textContent = "";
//需要将i重置为0
i=0
while ( ( elem = nodes[ i++ ] ) ) {
fragment.appendChild( elem )
}
console.log(fragment.childNodes,'fragment105')
return fragment;
}
let ajQuery={}
jQuery.each({
//例:'<tr><td>test1</td></tr>'
//源码6011行-6019行
// 在被选元素的结尾插入指定内容
/*append的内部的原理,就是通过创建一个文档碎片,把新增的节点放到文档碎片中,通过文档碎片克隆到到页面上去,目的是效率更高*/
append: function(nodelist, arguments) {
//node是由domManip处理得到的文档碎片documentFragment,里面包含要插入的DOM节点
let callbackOne=function( node ) {
console.log(node,'node149')
//this指的就是$("xxx")
//1:元素节点,11:DocumentFragment,9:document
if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
//table插入tr的额外判断
//target默认情况是selector,即document.querySelectorAll(".inner")
let target = manipulationTarget( this, node )
console.log(target,node.childNodes,'node147')
//append的本质即使用原生appendChild方法在被选元素内部的结尾插入指定内容
target.appendChild( node );
}
}
console.log(nodelist,arguments,'this120')
return domManip( nodelist, arguments, callbackOne );
},
},
function(key, value) {
ajQuery[key] = function(nodelist, arguments) {
console.log(nodelist,'nodelist128')
return value(nodelist, arguments);
}
}
)
$('#test1').click(function(){
let innerArr=document.querySelectorAll(".inner")
// ajQuery.append(innerArr,'<tr><td>test1</td></tr>')
// ajQuery.append(innerArr,"<script>alert('append执行script')")
/*$.append() 会执行script*/
$('.inner').append("<script>alert('append执行script')")
// let script = document.createElement( "script" )
// script.text = "alert('append执行script')"
// document.head.appendChild( script ).parentNode.removeChild( script );
})
</script>
</body>
</html>