〽️「こうしたい」ときのホームです。
let timer; widow.addEventListener("resize",function() { clearTimeout(timer); timer = setTimeout(処理関数,タイムラグ); })
こんにちは、「ふ」です。
「画面幅が変更されたとき、特定の処理を行う」実装を試みます。
考えられるのが、ウィンドウのサイズが変わったらそのイベントをキャッチして、関数を呼び出す。
window.onresize = function() { 「「3 〜処理〜 」」 }
windowオブジェクトのonresize属性に対し、関数呼び出しを指定した例です。
またはイベントリスナを使用して、
window.addEventListener("resize",function() { 「「3 〜処理〜 」」 });
としても良いのですが、これらの方法には問題があります。
上記の指定方法において、ユーザが手動で画面幅を変化させた場合。
操作中は常にリサイズイベントが発生し、そのつど関数が呼ばれてしまいます。
もし呼び出す処理が時間のかかるものであれば、処理が重複したり、ブラウザのメモリに負担をかけることにもなります。なによりブラウザの見た目がよくありません。
ブラウザ側においても、このイベント大量発生問題については対策がなされているようですが、実験してみたところ、十分なものではありませんでした。
window.addEventListener("resize",()=> alert("リサイズしたよ"));
⬆︎は画面のリサイズイベントが起きると「リサイズしたよ」とalertを出すもの。
短いコードなのでみなさんのほうでも実際に試してみるとわかります。
これを適用させた上で画面サイズを手動で変更させた場合、alertは複数回表示されてしまいました。2回以上のリサイズイベントがキャッチされてしまっている、ということを意味します。
画面幅変更処理が終わったときに、1度だけ処理を実行したい。
そこで今回はタイマー処理を使って、
画面リサイズ完了を検知して、一度だけ関数を呼び出す
方法について紹介していきます。
〽️ 使い方とメリットについて解説。
それでは仕組みについて考えていきましょう。
はじめに、「画面幅変更が完了した」とみなす条件をどうするか。
そもそもユーザが手動で画面幅を変更させているとき、イベントはどのように監視されているのでしょうか。
resizeイベントが発生するたびに、そのtimeStamp(イベントが発生した時間)を出力してみます。
window.addEventListener("resize",(event)=>console.log(event.timeStamp));
⬆︎のようなscriptを用意しました。そのうえで、手動で画面幅を変化させてみます。
1095.6000000238419 1118.1999999284744 1140.7999999523163 1159 1173.6999999284744 1192.8999999761581 ....
consoleには⬆︎のように出力されました。数値の単位はms(1/1000秒)です。
これで見るとユーザが連続的に画面をリサイズさせているとき、少なくとも0.1秒未満の間隔でresizeイベントが発生しているようです。
ではそれよりも十分に大きな間隔〜たとえば0.3秒間イベントが検知されなかった場合、「リサイズの操作が完了した」とみなすことができます。
これをリサイズ完了の条件としましょう。
「0.3秒間イベントが発生しなかった」ことをタイマー関数におとしこんでいきます。
setTimeout(処理関数,300);
setTimeoutメソッドは、一定時間後に関数を実行させるという「タイマー処理」です。
第1引数には呼び出す関数、第2引数にはタイムラグとなる時間を指定します。
タイムラグの値に、先ほど定義した0.3秒(300ms)を指定したとき。
このタイマーは、リサイズイベントが発生したのち0.3秒の間、外部からの変更などがなければ、処理関数がコールされます。条件を満たしたタイマーができました。
ただし。先ほど実験したように、ユーザが手動で画面幅を変化させているときには0.1秒未満の頻度でリサイズイベントは起こってしまっています。
もし、
window.addEventListener("resize",()=>setTimeout(処理関数,300));
⬆︎のように、リサイズイベントにsetTimeoutのタイマーを直接呼び出させた場合。
最初のタイマー完了を待たずに、次々と新たなタイマーが作成されていくことになります。
当然このままでは、処理関数もどんどん呼び出されてしまうことに。
ではこうするとどうでしょうか。
もし最初の0.3秒が完了する前に次のリサイズイベントが発生したら、1つ前のタイマーを無効にしてしまう。
無効にされたタイマーは、関数を呼び出すこともできなくなります。
0.3秒以内に次のリサイズイベントが発生する→つまり手動でリサイズが行われている間は、そのつど前回のタイマーをキャンセルさせていく。
0.3秒以内にリサイズイベントが発生しなくなった→これは、手動リサイズが完了したことを意味します。
手動リサイズが完了したときの最後のタイマーのみ有効にすれば、処理関数も1度だけしか呼び出されない、ということになります。
「0.3秒を判断基準とする」
「タイマーが重複したら前のものをキャンセル」
ルールと作戦が決まりました。実際にJavaScriptで実装していきましょう。
function comp() { alert("resizing completed"); }
リサイズ完了後に呼び出される関数「comp」を作成、内容はalertするだけのものにしました。
let timer;
またsetTimeoutのタイマーをキャンセルさせるには、あとからそのタイマーを呼び出せるようにしておく必要があります。
タイマーのid(識別子)を格納するための変数を、グローバルな場所で宣言しておきましょう。
以下、全コードです。
「「3//タイマーID格納用の変数を宣言」」「「1 ..@1@」」 let timer; 「「3//リサイズが行われたときのイベントリスナ」」「「1 ..@2@」」 window.addEventListener("resize",function() { 「「3//直前のタイマーをキャンセル」」「「1 ..@3@」」 clearTimeout(timer); 「「3//あらためてタイマーをスタートさせる」」「「1 ..@4@」」 timer = setTimeout(comp,300); }); 「「3//リサイズ完了後、呼び出される関数」」「「1 ..@5@」」 function comp() { alert("resizing completed"); }
@1@ setTimeoutのタイマーIDを格納するための変数を宣言しておきます。
@2@ windowがリサイズされたときの、イベントリスナを記述していきます。
@3@ 直前のタイマーをキャンセル。
もし前回のイベントから0.3秒未満の間に次のリサイズイベントが検知されたとき→つまり「手動リサイズが継続中」であるならば、直前のタイマーをclearTimeoutメソッドでキャンセルさせます。
「手動リサイズがすでに完了」している場合にもこのキャンセル処理は行われますが、すでに目的の関数は呼び出されているので、別に問題はありません。
@4@ @3@で直前のタイマーをキャンセルしたあと、あらためてここからタイマーを作成・スタートさせます。@3@@4@の処理をすることで常に最新のタイマーだけを有効にしておくことができます。
@5@ 前述のリサイズ完了後に呼び出される関数です。
これで実装はOKです。
〜ちなみにこのページ自体にもリサイズイベントを仕込んでいます。ここの部分が画面内に表示されている状態で、windowをリサイズしてみてください。alertが出てきます。
スマホのバヤイは、画面の縦横を変化させてみてくださいね。
最後までお読みくださり、ありがとうございました。
今回紹介したJavaScriptの内容は「debounce」と呼ばれているもので、連続してイベントが発生してしまう操作に対して「操作完了後に1回だけ処理をおこなう」手法です。
記事中では画面のリサイズを使って紹介してきましたが、他にもスクロールマウスムーヴなどのイベントにも利用することができます。ぜひ試してみてください。
ではまた〜 ♪
ベクターグラフィック、web、ガジェットなど。役立つ情報や観ていてたのしいページを書いていきたいと思います。