jQuery内部对<script>标签的处理
前言:
本文只分析 jQuery 调用 append(‘<script>
alert("xxx")’) 后,jQuery 对 <script>
的处理,
关于 append()、domManip()、buildFragment() 等处理 待插入元素的函数,
请看:当我调用了$().append()后,jQuery内部发生了什么?
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("<span>aaa</span>")
最后在 target.appendChild(elem)
中,插入的是 <span>aaa</span>
元素
console.log(elem.firstChild.nodeType,'target6016') //1:元素节点 target.appendChild( elem );
5、最后的最后
Github:
https://github.com/AttackXiao...
(完)