ウィンドウリサイズ完了時に一度だけ関数を実行する

2016年10月08日

カテゴリー:

ウィンドウをリサイズさせた時に何か処理をしたい事はよくありますよね。そういった場合、次の例の様に単純にwindow.onresizeイベントに処理したい関数を割り当てればとりあえずはリサイズ時に関数を実行させることは出来ます。

window.onresize = function () {
  console.log('hoge');
};

しかし、この場合リサイズしている間(=ウィンドウを動かしている間)何度も関数がコールされます。
それ自体は悪いことではないのですが、リサイズが「完了」した時に一度だけ処理を実行したいようなケースもあると思います。そういった場合、何度も同じ関数がコールされてしまうのは無駄な処理です。処理の内容が重いものであればなおさらです。

例えば、ウィンドウのリサイズが完了したタイミングで発火するイベントがあれば良いのですが、現状のブラウザではそんなものはありませんので、イベントハンドラ側で少し工夫する必要があります。

次のコードはウィンドウのリサイズ完了時に一度だけ処理を実行するサンプルです。

var timer = 0;

window.onresize = function () {
  if (timer > 0) {
    clearTimeout(timer);
  }

  timer = setTimeout(function () {
    console.log('window resized'); //ここに処理の内容が入る
  }, 200);
};

ポイントはタイマーを使っているところです。リサイズ完了時に実行したい内容をwindow.onresizeに割り当てた関数(イベントハンドラ)内に直接記述するのではなく、その関数の中でさらにsetTimeoutを使って指定時間後に実行するようにします。

ウィンドウのリサイズ中はwindow.onresizeのイベントハンドラが何度もコールされます。setTimeoutで指定された時間が経過する前(関数が実行される前)に再度イベントハンドラがコールされた場合は、タイマーをクリアし、再びセットし直すようにします。

このようにすれば、リサイズの最中はタイマーのセット&解除が繰り返されるだけで目的の関数は実行されず、リサイズ完了時(+setTimeoutの第2引数の時間)のタイミングで一度だけ実行されるようになります。

setTimeoutの第2引数は、目的の関数を実行させる遅延時間(ミリ秒)ですが、この値が大きすぎるとリサイズが完了してから処理が走るまでに体感的なタイムラグが大きいので少し不自然になってしまいます。逆に、値が小さすぎると、ゆっくりとリサイズさせた場合などにリサイズの最中に処理が走ってしまう可能性もあります。
ですので、ここの値は何通りかテストして一番しっくりくるものを採用するようにしてください。(私は大体100~200でいつも設定しています。)

最後に、先ほどのコードをもう少し使い回ししやすいように(自分のコピペ用に)書き直したコードを載せておきます。即時関数を使い、イベントハンドラ設定全体をスコープの中に包むことで外部のコードへ影響が出ないようにしています。

10行目のsetTimeoutの中身を適宜書き換えて利用します。

//リサイズ時の処理を定義
(function () {
  var timer = 0;

  window.onresize = function () {
    if (timer > 0) {
      clearTimeout(timer);
    }

    timer = setTimeout(function () {
      console.log('window resized'); //ここに処理の内容が入る
    }, 200);
  };
}());

このテクニック、既に色々なところで書かれているので今さら感があるのですが、毎回スクラッチで書いていたので、いい加減コピペできるようにしようと思ったのでメモ代わりに残しておきます。