scroll, resizeイベントの負荷を抑制する

特定の場所までスクロールしたら要素を表示させたり・動かしたり、またはリサイズに応じてレイアウトを再調整したりなど、JavaScriptではscrollやresizeに応じたイベントが頻繁に用いられますが、これらのイベントは断続的に処理を実行することになるので、制作者の意図せぬ形でユーザ側では負荷の高い処理になっているかもしれません。そこで、ちょっとの工夫だけでこれらのイベントの負荷を抑制できるポイントを紹介します。

スクロール時におけるサンプルコード

try { //IE9+, Other Browsers
	window.addEventListener( 'scroll', sample, false );
} catch (e) { //for IE8-
	window.attachEvent( 'onscroll', sample );
}
var timer = null;
function sample(){
	clearTimeout( timer );
	timer = setTimeout(function() {
		//処理内容
	}, 300 );
}

1行目から4行目までは、画面上(windowオブジェクトを指す)でのスクロールに応じてsampleという関数の実行をクロスブラウザ(主要ブラウザに対応させること)で設定しています。try~catch文は、try節のプログラム中で、なんらかのエラーが発生したときに、catch節を使ってエラーの内容を受け取ることができます。ここでは、try節の中でaddEventListener()メソッドを用いているので、Chrome, Firefox, Safari, Opera, IE9以上では問題はありませんが、IE8以下がaddEventListener()メソッドに対応していないため、catch節のattachEvent()メソッド(JavaScriptの標準化前にIEが独自に取り入れたメソッド)が用いられることになります。attachEvent()メソッドでは、HTMLのイベントハンドラの属性名のようにイベント名にonを前置します。

5行目のvar timer = null;は、timerという変数にnull値を代入しています。nullは何かを返すべきですが、返すものがない場合に意図的に使われるもので、ここでは処理前で返すものがないので、nullを代入しています。

6行目以降はsampleという関数の中身で、ここからがポイントになります。ここではclearTimeout()メソッドと先の変数timerに代入する形でsetTimeout()メソッドを用いています。実際に処理させたい内容は、このsetTimeout()メソッド内に記述します。

まず、画面上でスクロールが発生する度にsampleの関数が実行されることになりますが、このときsampleの関数内の処理をタイマーで指定ミリ秒後に実行させるようにしているのが変数timerに代入しているsetTimeout()メソッドです。タイマーで指定ミリ秒後に実行されるまでの間もスクロールは断続的に発生し続けますが、再度指定ミリ秒以内にsampleの関数が呼ばれたときにタイマーを取り消すようにするのが、clearTimeout()メソッドです。

このclearTimeout()メソッドとsetTimeout()メソッドの働きにより、通常はスクロール→処理実行→スクロール→処理実行と断続的に処理が行われるのを、スクロール→タイマー処理予約→スクロール→タイマー処理取消→タイマー処理予約→スクロール...中略...→最後のスクロール→タイマー処理取消→タイマー処理予約→処理実行という流れにできるので、断続的に発生するのはタイマーの予約と取消だけのため負荷を抑制することができます。

ただし、タイマーの設定時間によっては動きがかくかくすることになったりするため、滑らかになるようにタイマーの設定時間は善く善く調節をおこなってください。

jQueryを用いたコードに書き直した場合

jQueryであれば、先のクロスブラウザのコード部分が簡素に記述できます。

$(function(){
	var timer = null;
	$(window).on('scroll',function() {
		clearTimeout( timer );
		timer = setTimeout(function() {
			//処理内容
		}, 300 );
	});
});

なお、リサイズの場合は、タブレットやスマートフォンは縦・横の向きを変更したときぐらいしか発生し得ないのでそれほど気にする必要はありませんが、レスポンシブWebデザインを採用されている場合に、デスクトップにおいてはリサイズが断続的に行われる可能性が考えられるので、scrollの部分をresizeに、onscrollの部分をonresizeに変更することで、リサイズ時における断続的なイベントに対しても同様に負荷を抑制することができます。

なお、iOS7以前はタッチ時のスクロールに対して断続的に発生することがない(慣性スクロールが終了してからでしか発生しない)ようになっていましたが、iOSは8以降よりスクロールに対して断続的に発生させる仕様に変更されたので、タブレットやスマートフォンの各種のOSについてもこれらのイベントの負荷を抑制できる対策は有効です。

Updated / Published