jQuery内部对script标签的处理分析
分析 jQuery 调用 append(‘<script>
alert("xxx")’) 后,jQuery 对 <script>
的处理。
1、有这样一段代码:
<body>
<script src="jQuery.js"></script>
<div class="inner"></div>
<script>
$('.inner').append("<script>alert('append执行script')")
</script>
</body>
复制代码
2、当调用 $().append("<script>alert('append执行script')")
时,jQuery 内部的流程如下:
3、domManip 对 script 的处理
源码:
function domManip(collection, args, callback, ignored) {
args = concat.apply([], args);
var fragment,
first,
scripts,
hasScripts,
node,
doc,
i = 0,
l = collection.length,
iNoClone = l - 1,
value = args[0]
fragment = buildFragment(args, collection[0].ownerDocument, false, collection, ignored)
first = fragment.firstChild
if (fragment.childNodes.length === 1) {
fragment = first;
}
scripts = jQuery.map(getAll(fragment, "script"), disableScript); //script 1
/*判断是否包含<script>标签*/
hasScripts = scripts.length
//根据selector的个数循环
for (; i < l; i++) {
node = fragment;
if ( i !== iNoClone ) {
node = jQuery.clone(node, true, true);
//如果有script标签了,就将其复制到scripts中
if (hasScripts) {
jQuery.merge(scripts, getAll(node, "script"));
}
}
//此时的node已经是文本节点了
callback.call(collection[i], node, i);
}
//如果有<script>标签,就解析script
if (hasScripts) {
console.log(scripts, 'scripts5932') //script
doc = scripts[scripts.length - 1].ownerDocument; //document
jQuery.map(scripts, restoreScript);
for (i = 0; i < hasScripts; i++) {
node = scripts[i];
if (rscriptType.test(node.type || "") &&
//https://www.cnblogs.com/gongshunkai/p/5905917.html
!dataPriv.access(node, "globalEval") &&
jQuery.contains(doc, node)) {
//这边是处理<script scr=''>的情况的
if (node.src && (node.type || "").toLowerCase() !== "module") {
// Optional AJAX dependency, but won't run scripts if not present
if (jQuery._evalUrl) {
console.log('_evalUrl', '_evalUrl5950')
jQuery._evalUrl(node.src);
}
}
//一般走的这边
else {
console.log(doc, node.nodeType,node, 'DOMEval5954') //document 1 <script>alert('append执行script')< /script>
DOMEval(node.textContent.replace(rcleanScript, ""), doc, node);
}
}
}
}
return collection;
}
复制代码
解析:
(1)domManip 的 buildFragment() 返回文档碎片 fragment 后,会调用 domManip 自定义的 callback() 回调函数,在 <div class="inner"></div>
中插入 <script>alert('append执行script')
的文本
(2)之后,根据定义的变量 scripts 的长度,判断是否有 <script>
标签
scripts = jQuery.map(getAll(fragment, "script"), disableScript)
复制代码
(3)如果待插入元素有 <script>
标签的话,对该元素进行处理(restoreScript
)
//去除type标签
function restoreScript( elem ) {
if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) {
elem.type = elem.type.slice( 5 );
} else {
elem.removeAttribute( "type" );
}
return elem;
}
复制代码
(4)如果 <script>
没有属性 src 的话,就执行 DOMEval()
方法:
① jQuery 自个儿生成 <script>
标签
② 添加 待插入元素的文本
③ 执行 appendChild() 方法
//解析script标签
//code:alert('append执行script') #document
//doc:document
//node:<script>alert('append执行script')< /script>
function DOMEval(code, doc, node) {
doc = doc || document;
let i
//创建script标签
let script = doc.createElement("script")
//添加文本
script.text = code;
if (node) {
//i:type/src/noModule
for (i in preservedScriptAttributes) {
if (node[i]) {
script[i] = node[i];
}
}
}
//解析script核心代码
doc.head.appendChild(script).parentNode.removeChild(script);
}
复制代码
本质:可以看到 jQuery 解析 <script>
的本质即
document.head.appendChild(script).parentNode.removeChild(script)
复制代码
4、domManip 的 callback 函数会分两种情况处理 待插入元素
(1)$('.inner').append("<script>
alert('append执行script')") 最后在 target.appendChild(elem)
中,插入的不是 <script>
元素,而是 文本"<script>alert('append执行script')"
//源码6113行左右
append: function() {
return domManip( this, arguments, function( elem ) {
...
...
最后
console.log(elem.firstChild.nodeType,'target6016') //3:文本节点
target.appendChild( elem );
}})},
复制代码
(2)$('.inner').append("aaa") 最后在 target.appendChild(elem)
中,插入的是 <span>aaa</span>
元素
console.log(elem.firstChild.nodeType,'target6016') //1:元素节点
target.appendChild( elem );
作者:凉城以北Promise
链接:https://juejin.im/post/5ca04598f265da309b7801b7
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。