zoukankan      html  css  js  c++  java
  • JQuery html API支持解析执行Javascript脚本功能实现-代码分析

    JQuery html用法(功能类似innerHTML)

          开发中需要使用Ajax技术来更新页面局部区域, 使用的方法是ajax获取html代码段(字符串),然后将这个html代码段作为参数,传入目标DOM(JQuery对象)的JQuery html接口,此语句执行后, 会将html代码段解释执行, 显示出html代码段描述的页面控件。 例如:

    <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代码可以被执行, 这个是为什么? 是通过什么方式实现?

      通过js原生的innerHTML属性设置包括js代码段字符串,js代码不能执行。原生js接口中只能通过 eval 来执行js代码,猜测html的实现最终是调用这个函数来执行。先通过JQuery解析出js代码段中的文本信息,然后执行。

      测试JQuery append添加包括js代码段的字符串,js也可以被执行。

    代码例子:

    <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接口相关实现

     1、 html 接口实际上是, 将入参value传递到 access 接口中, 直接调用access, access的返回值就是 html的返回值。

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

    2、 access 函数分析

    此函数为多功能函数, 由入参决定实现的功能, 其他api如 text css attr 都是调用此接口实现,

    主要作用为, 将入参fn(一个函数)作用到elems上执行, key和value都可以作为fn执行的入参, 返回值还是elems。

    // 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, 如下代码,

            if ( bulk ) {
                // Bulk operations run against the entire set
                if ( raw ) {
                    fn.call( elems, value );
                    fn = null;

    返回elems:

        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;

    3、 下面看看 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代码执行的关系。

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

    5、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>
  • 相关阅读:
    DAY 223 GIT
    swooleHTTP
    swooleWebSocket
    swooleUDP
    swoole异步MySql
    swooleTCP
    谈谈继承的局限性
    也谈过程决定质量
    谁该为参数负责
    使用function改进设计
  • 原文地址:https://www.cnblogs.com/lightsong/p/3997306.html
Copyright © 2011-2022 走看看