elem.unbind();超重要
状況
jQueryバリバリのウェブサイトを作っていたら、ページを開くたびに表示速度が遅くなる、という問題が起きた。いろいろ調べてみると、IE6ではイベントを実装するとメモリリークを起こすらしく、あーこれはもう仕方ないなーと思っていたんですよ。
原因
んで、ちょっと時間ができたので、この時のJSの組み方が汚かったということもあり、再コーディングを試みることにして、ついでに具体的にどの部分でメモリリークが発生しているのかを確認してみたところ、どうもロールオーバー時に画像を切り替えるために使っている elem.hover(func, func); を書くか否かで明らかな差が出まして。もちろん循環参照を避けるために、func部分は無名関数にはしてませんよ。とりま、jQueryのhover();ってメモリリーク起こすんだー、と。あーでもそれがライブラリの仕様じゃ、これはもう仕方ないなーと思っていたんですよ。
解決策1
とはいえ、放置してたら再コーディングしている意味が無いので、別案を試そうと。例えばjQueryを使わず、ネイティブのJSで組むとか。しかしこれがやたら面倒くさい。というか、IE6が面倒くさい。まず、IEはaddEventListener();ではなくattachEvent();を使わなければいけない。つまり条件式でIEとそれ以外で分けなければいけない。しかも循環参照を避けるために、無名関数を使わないようにすると、thisに対象の画像のDOMなりなんなりが入ってくれないのですよ。んでarguments[0]を使って、Eventを拾ってきた所、どうもIEではEvent.currentTargetが使えないのでやっぱりthisにあたる何かが拾えないと。
//Blog記述上、それっぽく書き直してるので、このままでは動かないかもねー if(isIe()){ // isIe();はIE判定用の自作関数 elem.attachEvent("onmouseover", onmouseoverfunc); }else{ elem.addEventListener("mousemover", onmouseoverfunc); } var onmouseoverfunc = function(){ alert(this.src); // output: ie - null, fx - http://.....jpg var _event = arguments[0]; alert(_event.currentTarget); // 確かエラー(覚えてない) }
いろいろやれる手はあるようだけれでも、IE6の面倒くさい部分を解決するためにIE6のために面倒くさいことをするのが嫌になったので、この方法を諦める。
解決策2
やっぱり放置かなーと思ってたなか、Event Listener: イベントリスナ [JavaScript / DOM]を読んでて、「revemoEventListener();」とか「detachEvent();」とかについて書いてあるのを見て、「そういえばASでも使わないイベント消しておかないと重くなるしなーEvent.ENTERFRAMEとか…JSでもちゃんと消すべきかー。本来ならページ遷移したときに、全部無かったことになるもんなんだけど、IE6はメモリリークしてそれができないみたいだし」と思いたち、jQueryで設定したイベントの削除方法について調べた所、「それ、elem.unbind();でできるよ」という情報を見つけたので試してみることに。しかし、ロールオーバーアクションという性質上、イベントの削除可能タイミングが「別のページへ遷移した時」となるので、windowがunloadイベントを発行した時点でunbindするようにする。
//Blog記述上、それっぽく書き直してるので、このままでは動かないかもねー $(function(jQuery){ var hoverimgarr = []; $("img").each(hoverfunc); var hoverfunc = function(){ $(this).hover(hoveron, hoveroff); hoverimgarr.push($(this)); } var hoveron = function(){...} var hoveroff = function(){...} var unloader = function(){ for(var i = 0; i < hoverimgarr.length; i++){ $(hoverimgarr[i]).unbind("mouseenter").unbind("mouseleave").unbind("mouseover").unbind("mouseout"); //多分hover();はmouseenterとmouseleaveとを使っているので、後ろ2つはいらない気がするけど、 //圧縮前のソースコード落として確認してないので適当 } } $(window).unload(unloader); });
参考
確認
さて、実際これでどうかとIE6にて確認してみたところ、極端な使用メモリ量の増加は見られず、また、時として前回見た際の使用メモリ量を下回ることもあったので、おそらくメモリリークは抑えられていると思われる。unloadにunbind();つけるのを面倒臭がって避けてたけど、やっぱ重要なんだなー