jquery ie6内存泄露innerHTML使用注意

来源:互联网 发布:小二黑结婚知乎 编辑:程序博客网 时间:2024/04/30 01:19

问题代码:

首先看下面一段代码:

Html代码
  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"   
  2.     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
  3. <html>  
  4.     <head>  
  5.         <meta content="text/html;charset=utf-8" http-equiv="content-type" />  
  6.         <script src="jquery-1.3.2.js" type="text/javascript">  
  7.         </script>  
  8.         <style type="text/css">  
  9.             #inner {   
  10.                 margin:0 auto;   
  11.                 width:150px;   
  12.                 height:50px;   
  13.                 border:1px solid green;   
  14.             }   
  15.         </style>  
  16.         <script type="text/javascript">  
  17.         $(function(){   
  18.             $("#inner").click(function(){   
  19.                 //错误,引起内存泄露   
  20.                 $("#test")[0].innerHTML="";   
  21.                    
  22.                 //正确做法   
  23.                 //$("#test").empty();   
  24.             });   
  25.                
  26.         });   
  27.         </script>  
  28.         <title>测试</title>  
  29.     </head>  
  30.     <body>  
  31.         <div style="height:500px;width:500px;border:1px solid red;padding-top:100px;" id="test">  
  32.             <div id="inner">click to remove me</div>  
  33.         </div>       
  34.     </body>  
  35. </html>  

场景:

inner div 就是常见的我们可能使用 ajax 从服务器动态构造的元素,或者注入的片断html,效果为点击 inner 后就把它以及它的兄弟清除出去,依照传统的思路,直接 innerHTML =“” 就好了 ,可能你会想到所有删除元素的事件监听器应该清楚掉,但是对于除了 ie6 (引起内存泄露)以外的浏览器,清除事件监听器不是必要的 。



jquery 机制:

但是可以确定的是在 jquery 中用 innerHTML 的方法来清空元素,是必然会导致内存泄露的,由于 jquery 对于同一元素多事件处理没有直接采用浏览器事件模型,而是自己缓存事件,遍历触发,以及便于 trigger 程序触发 :

Js代码
  1. // Init the element's event structure   
  2.         var events = jQuery.data(elem, "events") || jQuery.data(elem, "events", {}),   
  3.             handle = jQuery.data(elem, "handle") || jQuery.data(elem, "handle"function(){   
  4.                 // Handle the second event of a trigger and when   
  5.                 // an event is called after a page has unloaded   
  6.                 return typeof jQuery !== "undefined" && !jQuery.event.triggered ?   
  7.                     jQuery.event.handle.apply(arguments.callee.elem, arguments) :   
  8.                     undefined;   
  9.             });  

 

采用 data 方法,将一些数据关联到了元素上面,上述事件即是采用该机制缓存事件监听器。


那么就可以知道,直接 innerHTML=“” 而不通知 jquery 清空与将要删除元素关联的数据,那么这部分数据就再也释放不了了,即为内存泄露。




解决:

jquery 已经想到了这一方面,提供了 empty 函数,其思想就是,对节点的所有子,孙,重孙.....节点( $("*",this) ),先清空它们关联的数据,再进行节点的删除:

Js代码
  1. remove: function( selector ) {   
  2.         if ( !selector || jQuery.filter( selector, [ this ] ).length ) {   
  3.             // Prevent memory leaks   
  4.             jQuery( "*"this ).add([this]).each(function(){   
  5.                 jQuery.event.remove(this);   
  6.                 jQuery.removeData(this);   
  7.             });   
  8.             if (this.parentNode)   
  9.                 this.parentNode.removeChild( this );   
  10.         }   
  11.     },   
  12.   
  13.     empty: function() {   
  14.         // Remove element nodes and prevent memory leaks   
  15.         jQuery(this).children().remove();   
  16.   
  17.         // Remove any remaining nodes   
  18.         while ( this.firstChild )   
  19.             this.removeChild( this.firstChild );   
  20.     }  

 

jquery导致IE6崩溃的继续研究

我在之前的一个帖子里汇报过jquery的ajax可能导致浏览器崩溃,详情见http://bbs.jquery.org.cn/read.php?tid-178.html

经过一系列的测试,我认为,jquery可能存在较严重的,导致浏览器崩溃的BUG

事件经过
1. 我在一个项目中引用了jquery,多数都是用于ajax请求的,在IE6会导致浏览器崩溃(IE7不会,经过10台以上计算机的测试,都存在,不是个别现象),初步认为是jquery的ajax并发导致的


2. 使用我以前封装的ajax类(也是应用于项目过的),同样会有这个问题,排除ajax问题。

3. 在网上发现,js内存溢出和窗口句柄的占用可能导致IE崩溃,于是重写方法,使用对象化,并在最后清除内存,还是会崩溃。

4. 最后,不使用任何jquery方法,采用原始的javascript,没有再崩溃了。。。。

使用的是jquery 1.1.2 pack版,已验证,源码版和pack版都有这样的问题,尚未验证1.1.1版有没有这个问题。

附件是测试的demo,经过简化处理,数据是读取的xml。
calendar_OOP_Jquery.htm 采用对象化的js,使用了jquery。不出10次就会崩溃
calendar_noOOP_Jquery.htm 使用jquery。不出10次就会崩溃
calendar_noOOP_noJquery.htm 没有采用面向对象js,没有使用jquery。测试到300次没有崩溃

日历算法的代码可以无偿使用、传播和修改,如果发现有什么BUG,请和我联系cimmicola#163.com


我实在是无语了。仅仅是使用了$选择器和html()方法。

如何避免JQuery Dialog的内存泄露

Posted on 2010-06-18 17:18 Kevin-moon 阅读(1667) 评论(8) 编辑 收藏 所属分类: Web前端技术
  对于页面来说,JQuery中的Dialog从效果上来说还可以,而且使用简单,只要短短几行绑定的代码就可以实现弹出效果。
代码
$('#dialog').dialog({
                 autoOpen: 
false,
                 width: 
600,
                 buttons: {
                     
"Ok": function() {
                         $(
this).dialog("close");
                     },
                     
"Cancel": function() {
                         $(
this).dialog("close");
                     }
                 }
             });

在一些JS交互性不多的一般页面来说,没有任何问题!但是对于交互性强的,需要动态加载与释放DOM的页面来说,它就是一个悲剧的东西!为什么这样说?大家看下下面的例子:

  一段简单的代码,一个DIV是通过动态加载到页面上,然后对该DIV用Dialog进行绑定,以达到弹出的目的!下面的test元素就是<div id="test"></div>。

代码
function TestAppend() {

             $(
"#test").append('<div id="dialog"><div id="fileQueue"></div> <input type="file" name="uploadify" id="uploadify" />' +
                        
'<a href="javascript:upload();">上传</a>' +
                        
'<a href="javascript:$(#uploadify).uploadifyClearQueue()">取消上传</a><div>');

             $(
'#dialog').dialog({
                 autoOpen: 
false,
                 width: 
600,
                 buttons: {
                     
"Ok": function() {
                         $(
this).dialog("close");
                     },
                     
"Cancel": function() {
                         $(
this).dialog("close");
                     }
                 }
             });
             
             
return false;
         }

  接着,我需要删除该DOM元素,一般来说,正常的做法都是$("#test").empty();这行简单的代码就完成了!这样有效吗?!当执行完这样代码后,你再用$('#dialog')来获取dialog元素,郁闷的事情发生了,既然获取到了!为什么!不是已经empty了吗!

  下面我们来看下这一悲剧是如何造成的,

  我们把注意点放到$('#dialog').dialog上面,然后看看JQuery的实现代码是如何写的,当我们跟踪代码到dialog类中的_create方法的时候,问题的原因找到了,看下面这段代码:

_create
uiDialog = (self.uiDialog = $('<div></div>'))
                .appendTo(document.body)
                .hide()
                .addClass(uiDialogClasses 
+ options.dialogClass)
                .css({
                    zIndex: options.zIndex
                })
                
// setting tabIndex makes the div focusable
                
// setting outline to 0 prevents a border on focus in Mozilla
                .attr('tabIndex'-1).css('outline'0).keydown(function(event) {
                    
if (options.closeOnEscape && event.keyCode &&
                        
event.keyCode === $.ui.keyCode.ESCAPE) {
                        
                        self.close(
event);
                        
event.preventDefault();
                    }
                })
                .attr({
                    role: 
'dialog',
                    
'aria-labelledby': titleId
                })
                .mousedown(function(
event) {
                    self.moveToTop(
falseevent);
                }),

  它既然也动态创建一个div,而且把该div加到了Body上面,然后把dialog中的元素从<div id=test>中移除,加入到该新的div中.....

  这就是为什么我们$("#test").empty()后,却对内部的dialog没有起作用了!而且这有一个最不好的一个地方,也是最容易出现内存泄露的地方:它动态的在Body中创建了一个div,这样如果窗体不关闭的话,而你又在不察觉的情况下不断的使用上面的TestAppend方法来动态加载DOM,就会创建N个这样的div!

  其实这个问题郁闷的地方不是在如何解决,而且隐藏的很深,很难发现!那么发现之后解决起来就变的简单多了:

if ($('#dialog')[0]) {
    $(
'#dialog').parent().empty();
    $(
'#dialog').parent().remove();
}

 当前加上这段后代码后,再做$("#dialog")来测试下,期望的结果终于出现了!dialog元素消失了!

Jquery1.3的在ie6下ajax缺陷

文章分类:Web前端
这段时间公司开始更换os,从windows 2000变成ubuntu,我们的网站也跟着修改,css就到处找hack,javascript就去找jquery, 测试发现jquery的ajax请求直接造成部分ie6崩溃,只要是ie6,无论是什么操作系统,一个都没有逃掉,假如是只有自己写的ajax请求还无所谓了,但是那么多的jquery ajax插件不能用,实在是痛苦,开始找是否是ie6的问题,看了ajax:请使用最新版本的xmlhttp,了解到ie6在不同的os可能使用不同的 xmlhttprequest,如是乎到windows 2000下找,发现是msxml2.xmlhttp.4.0,windows 2003下,发现是msxml2.xmlhttp.5.0,接着去找jquery.js中的写法



Javascript代码 < width="14" height="15" src="http://iamliming.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" pluginspage="http://www.macromedia.com/go/getflashplayer">
  1. xhr:function()  
  2. {  
  3.        return window.activexobject ? new activexobject("microsoft.xmlhttp") : new xmlhttprequest();  
  4. }  

 






直接崩溃掉,从上篇文章中了解到,microsoft.xmlhttp为ie下最早的一个xmlhttp版本,看来jquery的开发者也有意的将ie6系列的浏览器抛弃

ie7 已经开始支持xmlhttprequest.

最后将这段代码改写成如下模样

Javascript代码 < width="14" height="15" src="http://iamliming.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" pluginspage="http://www.macromedia.com/go/getflashplayer">
  1. var orequest;  
  2.  if(typeof xmlhttprequest=="undefined" && window.activexobject)  
  3.     {  
  4.        var arrsignatures = ["msxml2.xmlhttp.5.0","msxml2.xmlhttp.4.0","msxml2.xmlhttp.3.0","msxml2.xmlhttp","microsoft.xmlhttp"];  
  5.         for(var i=0;i<arrsignatures.length;i++)  
  6.         {  
  7.             try  
  8.             {  
  9.                orequest = new activexobject(arrsignatures[i]);  
  10.              return orequest;  
  11.             }  
  12.             catch(oerror)  
  13.             {  
  14.             }  
  15.            }  
  16.    }  
  17. else  
  18.    orequest=new xmlhttprequest();  
  19. return orequest