JQuery html API支持解析執行Javascript腳本功能實現-代碼分析

JQuery html用法(功能相似innerHTML)

      開發中須要使用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>

 

JQuery html能夠解析執行js腳本,爲啥?

  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接口解析執行js腳本 - 關鍵代碼分析

查看jquery源代碼,分析html接口相關實現jquery

 一、 html 接口其實是, 將入參value傳遞到 access 接口中, 直接調用access, access的返回值就是 html的返回值。

    html: function( value ) {
        return access( this, function( value ) { 
.......
}, null, value, arguments.length );

 

二、 access 函數分析

此函數爲多功能函數, 由入參決定實現的功能, 其餘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 接口調用access時候,實現的fn函數

當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函數分析

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 );
            }
        });
    },

 

五、domManip實現分析

首先,調用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, "" ) );
                            }

 

總結 jquery html執行js的原理

使用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>
相關文章
相關標籤/搜索