JavaScript で window リサイズ終了後のタイミングをハンドリングする方法

2014.06.03

少し前に社内のメンバーから表題の件について相談を受けたことがあります。その時は「さぁ…、なんか面倒臭そうだし、ちょっと分かりませんわー。」と超絶冷たい応対をしてしまったわけですが、さっきふと閃いたことを試したところアッサリ実現できたので、備忘録としてここに書き記しておくとします。

setTimeout を利用する

 先にコードを見てみましょう。こんな感じになります。

var resizeTimer;
var interval = Math.floor(1000 / 60 * 10);

window.addEventListener('resize', function (event) {
  console.log('resizing');
  if (resizeTimer !== false) {
    clearTimeout(resizeTimer);
  }
  resizeTimer = setTimeout(function () {
    console.log('resized');
    // do something ...
  }, interval);
});

windowオブジェクトに対してresizeイベントに対するリスナー関数を定義します。ここまではごく普通ですが、これだけだとウィンドウサイズをぐりぐり動かしている間にもリスナー関数内の処理が実行されてしまいます。そこで上記のようにsetTimeout() 用の変数を用意し、これの中身を都度チェックすることで処理したいコードを実行するかどうかを判別させるというわけです。

  1. ウィンドウサイズを動かす操作
  2. resize イベントが発火
  3. setTimeout()が呼び出され、n 秒後に処理を実行すると定義される
  4. (1周目おわり)
  5. (再び)ウィンドウサイズを動かす操作
  6. resize イベントが発火
  7. setTimeout() が既に定義されているので、clearTimeout()で処理内容を消去し、実行されないようにする
  8. (再び)setTimeout()が呼び出され、n 秒後に処理を実行すると定義される
  9. ……
  10. ウィンドウサイズを動かす操作が終了
  11. setTimeout() で定義された処理が実行される

setTimeout() に指定した処理が実行されるまでの時間について少し考えてみます。FPS を 60 とすると1000msec / 16Hz = 16.6msec となり、1フレーム辺りの時間は約 16.6 ミリ秒です。つまり仮に setTimeout() に 16 と指定するとリサイズ操作が終わってから 16 ミリ秒後に処理が実行されるということになります。しかしこれは相当に高い目標値であり、実際に試してみてもとても期待通りには動きません。ウィンドウのリサイズはDOMの書き換えを引き起こす処理であり、当然描画されているDOMの数が多ければ再描画にかかる負荷も高くなります。具体的にどれくらいの値を指定するのが良いかはケースバイケースですが、最低でも上のコードにある数値以上を指定するのが良いでしょう。