【JavaScript】スクロール処理を間引くthrottle関数を自作する方法。

JavaScript-throttle関数自作_eyecatch

Lodashなどのサードパーティーのライブラリーを使わずに、JavaScriptのみでthrottle関数を自作する方法です。

JavaScriptでスクロールイベントなど、連続で繰り返し発火するイベントを使用する際には、負荷を軽減させるためにthrottleやdevounceを使うのは有名です。

いわゆる“間引き処理”に使う機能ですが、throttle1つの為に巨大なライブラリーを読み込むのがイヤだったので、自前で用意する方法を調べました。

Lodash.js等のライブラリーなしでthrottleを自作・用意する方法です。

JavaScriptでthrottleを自作する方法

当記事をお読みの方は既にご存じかと思いますが、JavaScriptのscrollイベントや、mousemoveイベントはそのまま使用すると画面スクロールやマウスカーソルが動くたびに連続で発火します。

百聞は一見に如かずとも言いますので、簡単なサンプルコードを作ってscrollイベントが連続で発火する様子をGIFにしてみました。

JavaScript-throttle関数自作_001

スクロールするごとにscrollイベントが発火する様子(GIF)

スクロールごとに画面のY座標を表示する簡単なものですが、こんな感じです。

サンプルは処理が軽いコードなので何の問題も起きませんが、ある程度複雑な処理をイベント毎に走らせるとなると、ブラウザに高負荷がかかってスクロールがカクつくなどの問題が出てきます。

そんな時にお世話になるのが処理を一定間隔で間引くthrottleという機能です。
簡単に言えば、scrollイベントのような連続発火するイベントを良い感じのタイミングごとに発火するように調整する機能で、よく「間引き」と言われるものです。

Lodash.jsなしでthrottleを使いたい

さて、ここから本題です。

以前scrollイベントを使って記事の目次の現在地を取得するコードを開発していたのですが、その際throttleを使うのに難儀しました。

というのもthrottleメソッドはlodash.jsなど、サードパーティーのライブラリーの中に同梱されており、このようなライブラリーを使うのが一般的な様子なのです。

throttleの使い方を調べると当たり前のように出てくる下記のようなコード。

window.addEventListener('scroll', _.throttle(fn, 1000));

この_.throttle()という書き方はlodash.jsの使い方みたいです。

throttle1つを使う為だけにライブラリー丸ごと読み込むのもイヤだなぁ…
と思っていたところ、lodash.jsはNode.jsのnpmコマンドを使ってインストールする必要があるそうで…そもそも共有サーバーなので無理です。

ここらへんでthrottleはサクッと使えるものではないことが判明…意外と面倒です。
まぁjQueryやサードパーティーのライブラリを読み込めばサクッと使えるんですが、サイトが重たくなりそうでイヤ!…という変なこだわりから詰まりました。

スポンサーリンク

JavaScriptのみで書いたthrottleのコード

う~ん困った…と思い途方に暮れていたところ、JavaScriptのみでthrottleを自作してコードを紹介して下さっている方がおられました!

外部リンク

そのサイト様がコチラ(↓)です。
throttleとdebounce|mille-feuille code

実際に使わせていただくことになったコードがこちらです。

function throttle(fn, delay) {
  let timerId;
  let lastExecTime = 0;
  return () => {
    const context = this;
    const args = arguments;
    let elapsedTime = performance.now() - lastExecTime;
    const execute = () => {
      fn.apply(context, args);
      lastExecTime = performance.now();
    }
    if (!timerId) {
      execute();
    }
    if (timerId) {
      clearTimeout(timerId);
    }
    if (elapsedTime > delay) {
      execute(); 
    } else {
      timerId = setTimeout(execute, delay);
    }
  }
}

まさにこれです!JavaScriptだけでthrottleを用意することができます・w・

コードについては作成者様のサイトをご確認下さい。
今回はthrottleのコードを使わせていただきましたが、JavaScriptのみでdebounceを自作するコードも掲載して下さっています。

用意したthrottleを使ってみる

コードの使い方についてはlodash.jsの使い方と同じです。
先ほどのコードを読み込み、throttle(callback, delay) の形で使います。

function myfunc(){
  console.log("スクロールしました!Y座標:" + window.scrollY);
  }

window.addEventListener('scroll', throttle(()=>myfunc(), 1000));

こんな感じです。
もちろん引数1000を変更することで処理間隔を調整することが可能です。

せっかくなのでこちらも実際の動作をGIFにしてみました。

JavaScript-throttle関数自作_002

throttleを使ってscrollイベントを間引いた様子(GIF)

記事冒頭のthrottleを使用していない時と比べると、scrollイベントが間引かれて発火回数が大幅に減少していることは一目瞭然ですね。

一応スクロールイベントなので、煮詰めるならFPS(リフレッシュレート)を設定したり、第三引数に{passive: true}も設定しておくと良いかもしれません。

const fps = 60;
window.addEventListener('scroll', throttle(()=>fn(), 1000 / fps), {passive: true});

FPSはカクつき防止のおまじない程度かも…
また、{passive: true}もタッチイベントじゃないので多分要りません。



まとめ

以上、scrollイベントなどの処理を間引くための機能であるthrottleを自前で用意する方法でした。

特にこだわりが無ければ、サードパーティーからthrottleを読み込んで使うのもアリですが、throttleやdebounceを単体で使いたい時は自作して用意するのもお勧めです。

-サイト構築
-

site-logo
ありがとうございました!
良かったらシェアしてネ・w・
当サイトはピンバックを受け付けます。
  • この記事を書いた人
ザキ

ザキ

X:@sologaku

プログラミングに興味があり情報系の大学を卒業。

新卒で社会人になるも数年で退職し今はフリーランスとして生きています。

少しでも誰かの役に立てれば...と思い、当サイトでIT関係のハウツーを発信しています。


ソロ学運営者:ザキの自己紹介

© 2020 sologaku