IE の window.resize イベントの問題を回避する
IE の window.resize イベントは、いくつかの問題を抱えています。
function onresize() { (何か) } window.attachEvent("onresize", onresize);
- うっかり無限ループする(無限に再描画が走る)
- 重い(特にIE6)
解決していきましょう。
無限ループ/無限リドロー問題
resize イベントハンドラの中で、document.body.innerWidht, innerHeight が変化するような操作を行うと、再度 resize イベントが発生し無限ループする現象が発生します(無限リロリロ)。
無限リロリロを回避するために、resize イベントハンドラ内でイベントをデタッチし、再アタッチを繰り返す方法があります(リアタッチ作戦)
function onresize() { window.detachEvent("onresize", onresize); // デタッチ (何か) window.attachEvent("onresize", onresize); // リアタッチ } window.attachEvent("onresize", onresize);
動作速度の問題
リアタッチ作戦は一見いけそうにみえますが、魔王(IE6)の前では所詮子供だましのようです。
この作戦を繰り返し行うと、「ある時点から resize イベント発生時にどんどん重くなる」という別の現象が発生します。
イベントをアタッチ ⇒ デタッチを繰り返すのは止めて、時間差によるイベントの集約を行います(一人時間差作戦)
var _globalLock = 0; function resize() { if (!_globalLock++) { setTimeout(function() { (コールバック) setTimeout(function() { _globalLock = 0; }, 0); // delay unlock }, 40); } }
さらに前へ
一人時間差作戦を組み込んだコードを走らせていると、今度は欲がでてきます「もっと速く/軽くできないか」。
プロファイラを走らせると、resize イベント自体が「とても重い」ということに気がつきます。体感でも resize イベントが多発する状況では目に見えて重くなります。
そこで、resize イベントを使用せず同様のことを実現してみます。
var _globalLock = 0; var _size = { w: 0, h: 0 }; var _ie = document.uniqueID; var _quirks = (document.compatMode || "") !== "CSS1Compat"; var _ieroot = _quirks ? "body" : "documentElement"; function getInnerSize() { var root = _ie ? document[_ieroot] : window; return { w: root.innerWidth || root.clientWidth, h: root.innerHeight || root.clientHeight }; } // resize agent function agent() { function loop() { if (!_globalLock++) { var size = getInnerSize(); if (_size.w !== size.w || _size.h !== size.h) { // resized _size = size; // update (コールバック) } setTimeout(function() { _globalLock = 0; }, 0); // delay unlock } setTimeout(loop, 100); } setTimeout(loop, 100); }
100ms ごとにブラウザのクライアント領域の大きさ(innerWidth, innerHeight)を監視するエージェントを用意し、変化があれば、コールバックします(エージェント作戦)
計ってみた
box-shadow: をレンダリングする JavaScript を埋め込んだページで速度を計ってみました。
IE6 + ASPIRE ONE(XP)
IE7 + HP G5000(VISTA)
IE8 + DELL VOSTRO 1000(XP)
まとめ
エージェント作戦は、IE7, IE8 では特にデメリットもないようですし、IE6 では 65ms(15%) の改善がみられます。
さらに高負荷なページで計測すると、最大で 40% ほどのレスポンスの改善が見られるケースもありました。
一人時間差作戦も簡潔に書けるので悪くはありません(良くも…無いかな)。
反省会
- 無限リロリロ, リアタッチ作戦, 一人時間差作戦, エージェント作戦
- 造語多し