博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
jQuery源码——.html()方法原理解析
阅读量:6212 次
发布时间:2019-06-21

本文共 6799 字,大约阅读时间需要 22 分钟。

在将字符串转化为html碎片时,一般会将字符串作为容器的innerHTML属性赋值。但innerHTML有很多局限性,比如我们想转化的字符串中有<script>标签并且包含一个立即执行的函数,如果将此字符串通过innerHTML转化为html碎片,<script>标签中的函数并不会被执行。

jQuery中的.html()函数可以弥补innerHTML的缺陷,我们看下这个方法是如何实现的。

其实原理很简单:正则匹配<script>标签,获取js函数,然后用eval()函数解析。jQuery在处理此工程中有几个细节值得学习。

首先看一下html()函数的主入口:

1 html: function( value ) { 2         return access( this, function( value ) { 3             var elem = this[ 0 ] || {}, 4                 i = 0, 5                 l = this.length; 6  7             if ( value === undefined ) { 8                 return elem.nodeType === 1 ? 9                     elem.innerHTML.replace( rinlinejQuery, "" ) :10                     undefined;11             }12 13             // See if we can take a shortcut and just use innerHTML14             if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&15                 ( support.htmlSerialize || !rnoshimcache.test( value )  ) &&16                 ( support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&17                 !wrapMap[ (rtagName.exec( value ) || [ "", "" ])[ 1 ].toLowerCase() ] ) {18 19                 value = value.replace( rxhtmlTag, "<$1>
" );20 21 try {22 for (; i < l; i++ ) {23 // Remove element nodes and prevent memory leaks24 elem = this[i] || {};25 if ( elem.nodeType === 1 ) {26 jQuery.cleanData( getAll( elem, false ) );27 elem.innerHTML = value;28 }29 }30 31 elem = 0;32 33 // If using innerHTML throws an exception, use the fallback method34 } catch(e) {}35 }36 37 if ( elem ) {38 this.empty().append( value );39 }40 }, null, value, arguments.length );41 },

1.  html()函数返回一个单例闭包access()函数,避免作用域污染;

2. 第14行,首先确定value是string类型,并且用 rnoInnerhtml.test( value ) 正则匹配value中是否包含<script>标签;

3. 第26行,首先清理容器的内容,然后将value作为容器的innerHTML属性赋值,然后将代表容器的局部变量elem赋值为0,跳过37行逻辑。有些同学会疑惑,将elem赋值为0为什么不会影响dom元素?这里面涉及到JavaScript中值类型和引用类型的区别,请自行查阅相关资料;

4. 第38行,如果value中包括<script>标签,则用append()方法进行后续操作。

那么append()函数是怎么处理的呢?

1 append: function() {2         return this.domManip( arguments, function( elem ) {3             if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {4                 var target = manipulationTarget( this, elem );5                 target.appendChild( elem );6             }7         });8     },

 

append()函数调用domManip()函数,回调函数中的参数elem是经domManip()函数处理后的documentFragment,domManip()内部代码如下:

1 domManip: function( args, callback ) { 2  3         // Flatten any nested arrays 4         args = concat.apply( [], args ); 5  6         var first, node, hasScripts, 7             scripts, doc, fragment, 8             i = 0, 9             l = this.length,10             set = this,11             iNoClone = l - 1,12             value = args[0],13             isFunction = jQuery.isFunction( value );14 15         // We can't cloneNode fragments that contain checked, in WebKit16         if ( isFunction ||17                 ( l > 1 && typeof value === "string" &&18                     !support.checkClone && rchecked.test( value ) ) ) {19             return this.each(function( index ) {20                 var self = set.eq( index );21                 if ( isFunction ) {22                     args[0] = value.call( this, index, self.html() );23                 }24                 self.domManip( args, callback );25             });26         }27 28         if ( l ) {29             fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this );30             first = fragment.firstChild;31 32             if ( fragment.childNodes.length === 1 ) {33                 fragment = first;34             }35 36             if ( first ) {37                 scripts = jQuery.map( getAll( fragment, "script" ), disableScript );38                 hasScripts = scripts.length;39 40                 // Use the original fragment for the last item instead of the first because it can end up41                 // being emptied incorrectly in certain situations (#8070).42                 for ( ; i < l; i++ ) {43                     node = fragment;44 45                     if ( i !== iNoClone ) {46                         node = jQuery.clone( node, true, true );47 48                         // Keep references to cloned scripts for later restoration49                         if ( hasScripts ) {50                             jQuery.merge( scripts, getAll( node, "script" ) );51                         }52                     }53 54                     callback.call( this[i], node, i );55                 }56 57                 if ( hasScripts ) {58                     doc = scripts[ scripts.length - 1 ].ownerDocument;59 60                     // Reenable scripts61                     jQuery.map( scripts, restoreScript );62 63                     // Evaluate executable scripts on first document insertion64                     for ( i = 0; i < hasScripts; i++ ) {65                         node = scripts[ i ];66                         if ( rscriptType.test( node.type || "" ) &&67                             !jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) {68 69                             if ( node.src ) {70                                 // Optional AJAX dependency, but won't run scripts if not present71                                 if ( jQuery._evalUrl ) {72                                     jQuery._evalUrl( node.src );73                                 }74                             } else {75                                 jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) );76                             }77                         }78                     }79                 }80 81                 // Fix #11809: Avoid leaking memory82                 fragment = first = null;83             }84         }85 86         return this;87     }

 

1. 第28行-55行,生成docmentFragment,并将<script>节点克隆以便后续的解析执行;

2. 第57行-79行,执行<script>内部的代码,注意75行的 node.text || node.textContent || node.innerHTML || "" ,这是兼容写法,即获取<script>标签内部的文本。

3. 第69行,如果<script>标签是引用外部资源,则请求资源url;

3. 第75行,如果<script>标签是行内代码,则调用globaleEval()函数执行<script>内部的逻辑,代码如下:

1 globalEval: function( data ) { 2         if ( data && jQuery.trim( data ) ) { 3             // We use execScript on Internet Explorer 4             // We use an anonymous function so that context is window 5             // rather than jQuery in Firefox 6             ( window.execScript || function( data ) { 7                 window[ "eval" ].call( window, data ); 8             } )( data ); 9         }10     },

 

转载地址:http://mnsja.baihongyu.com/

你可能感兴趣的文章
keepalived VS zookeeper
查看>>
类锁与对象锁,重入锁
查看>>
StratifiedShuffleSplit 交叉验证
查看>>
MySQL时间增加、字符串拼接
查看>>
***ThinkPHP中的常用方法汇总总结:M方法,D方法,U方法,I方法
查看>>
vue当前路由跳转初步研究
查看>>
[MicroPython]TurniBit开发板DIY自动窗帘模拟系统
查看>>
lerna基本试用
查看>>
python3 如何给装饰器传递参数
查看>>
scala函数返回值
查看>>
设计模式1-单例模式(Singletion)
查看>>
JFinal提示:java.lang.RuntimeException: dao 只允许调用查询方法
查看>>
浮点数正则表达式
查看>>
Creator开源游戏、插件、教程、视频汇总
查看>>
zxing的使用及优化
查看>>
PHP笔试题(11道题)详解
查看>>
2018第34周六
查看>>
mybatis-plus忽略映射字段
查看>>
Android 通过接口的方式去调用服务里面的方法
查看>>
什么是JavaBean、bean? 什么是POJO、PO、DTO、VO、BO ? 什么是EJB、EntityBean?
查看>>