開發中須要使用Ajax技術來更新頁面局部區域, 使用的方法是ajax獲取html代碼段(字符串),而後將這個html代碼段做爲參數,傳入目標DOM(JQuery對象)的JQuery html接口,此語句執行後, 會將html代碼段解釋執行, 顯示出html代碼段描述的頁面控件。 例如:javascript
<html> <head> <script type="text/javascript" src="./jquery.js"></script> <style> </style> </head> <body> <div>hello world!</div> <script type='text/javascript'> //假設ajaxHTMLStr是經過ajax得到的html代碼段 var ajaxHTMLStr = "<input type='button' value='click me'>"; $("div").html(ajaxHTMLStr); </script> </body> </html>
html接口對於入參內容的要求,能夠包括html標籤代碼,也能夠包括Javascript代碼段(使用 <script>標籤括起來的部分),執行的效果是, html標籤能夠顯示, 同時Javascript代碼能夠被執行, 這個是爲何? 是經過什麼方式實現?css
經過js原生的innerHTML屬性設置包括js代碼段字符串,js代碼不能執行。原生js接口中只能經過 eval 來執行js代碼,猜想html的實現最終是調用這個函數來執行。先經過JQuery解析出js代碼段中的文本信息,而後執行。html
測試JQuery append添加包括js代碼段的字符串,js也能夠被執行。java
代碼例子:node
<html> <head> <script src="./jquery.js"></script> </head> <body> <div name="template"> <select> </select> <input type="button" name="testBtn" value="click me"> </div> <script> var divhtml = "<script type='text\/javascript'>console.log('aa')<\/script>"; divhtml += "<div>aa</div>"; //只能執行html段, javascript代碼段忽略 //$("[name='template']").get(0).innerHTML = divhtml; //能執行html, javascript代碼 $("[name='template']").eq(0).html(divhtml); //能執行html, javascript代碼 //$("[name='template']").eq(0).append(divhtml); </script> </body> </html>
查看jquery源代碼,分析html接口相關實現jquery
html: function( value ) { return access( this, function( value ) { .......
}, null, value, arguments.length );
此函數爲多功能函數, 由入參決定實現的功能, 其餘api如 text css attr 都是調用此接口實現,ajax
主要做用爲, 將入參fn(一個函數)做用到elems上執行, key和value均可以做爲fn執行的入參, 返回值仍是elems。api
// Multifunctional method to get and set values of a collection // The value/s can optionally be executed if it's a function var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
對於html接口設置html代碼段字符串狀況,access中執行代碼, 會將fn做用到elems上,並帶着入參value, 以下代碼,app
if ( bulk ) { // Bulk operations run against the entire set if ( raw ) { fn.call( elems, value ); fn = null;
返回elems:dom
return chainable ? elems : // Gets bulk ? fn.call( elems ) : length ? fn( elems[0], key ) : emptyGet; return chainable ? elems : // Gets bulk ? fn.call( elems ) : length ? fn( elems[0], key ) : emptyGet;
當html如參爲空, 則表示獲取dom內的html代碼:
if ( value === undefined ) { return elem.nodeType === 1 ? elem.innerHTML.replace( rinlinejQuery, "" ) : undefined; }
判斷html代碼段中, 沒有script link 和 style, 則表示爲純html標籤代碼,使用innerHTML直接更新文檔:
// See if we can take a shortcut and just use innerHTML if ( typeof value === "string" && !rnoInnerhtml.test( value ) && ( support.htmlSerialize || !rnoshimcache.test( value ) ) && ( support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && !wrapMap[ (rtagName.exec( value ) || [ "", "" ])[ 1 ].toLowerCase() ] ) {
。。。。
elem.innerHTML = value;
。。。。
若是非上面兩種狀況, 則調用empty和append接口(也是JQuery API), 將html代碼段更新到文檔:
if ( elem ) { this.empty().append( value ); }
empty實現不爲咱們關注, 下面咱們重點關注append實現關於js代碼執行的關係。
append實現,直接調用 domManip 函數, 將append的入參做爲domManip函數的第一個參數, 將一個回調函數做爲第二個參數, 最後返回其返回值。
append: function() { return this.domManip( arguments, function( elem ) { if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { var target = manipulationTarget( this, elem ); target.appendChild( elem ); } }); },
首先,調用buildFragment,將arguments(html代碼段字符串)傳入構造一個 fragment DOM對象,此對象脫離文檔流,僅僅存在於內存中。
if ( l ) { fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this ); first = fragment.firstChild;
buildFragment: function( elems, context, scripts, selection ) { 。。。 safe = createSafeFragment( context ), 。。。 return safe; },
createSafeFragment
function createSafeFragment( document ) { var list = nodeNames.split( "|" ), safeFrag = document.createDocumentFragment(); if ( safeFrag.createElement ) { while ( list.length ) { safeFrag.createElement( list.pop() ); } } return safeFrag; }
其次此函數將html代碼轉換爲dom
// Convert html into DOM nodes } else { tmp = tmp || safe.appendChild( context.createElement("div") ); // Deserialize a standard representation tag = (rtagName.exec( elem ) || [ "", "" ])[ 1 ].toLowerCase(); wrap = wrapMap[ tag ] || wrapMap._default; tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[2];
而後此函數將 script dom節點的text內容拿去eval計算一下,支持科理解此問題的緣由了
if ( node.src ) { // Optional AJAX dependency, but won't run scripts if not present if ( jQuery._evalUrl ) { jQuery._evalUrl( node.src ); } } else { jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) ); }
使用innerHTML將 html代碼段(包括 html標籤字符串 和 js代碼段字符串)加入到DOM樹中, 而後獲取js的script節點的內容,調用eval執行。
<html> <head> <script src="./jquery.js"></script> </head> <body> <div name="template"> <select> </select> <input type="button" name="testBtn" value="click me"> </div> <script> var divhtml = "<script type='text\/javascript'>console.log('aa')<\/script>"; divhtml += "<div>aa</div>"; //只能執行html段, javascript代碼段忽略 $("[name='template']").get(0).innerHTML = divhtml; //腳本雖然沒有被執行,可是仍然被加入到DOM樹中, 能夠獲取腳本內容執行 var scriptDOM = $("script", "[name='template']"); console.log("nodeType="+scriptDOM.get(0).nodeType); var scriptStr = scriptDOM.text(); console.log("script code="+scriptStr); eval(scriptStr); //能執行html, javascript代碼 //$("[name='template']").eq(0).html(divhtml); //能執行html, javascript代碼 //$("[name='template']").eq(0).append(divhtml); </script> </body> </html>