Webアプリケーションを開発する際、頻繁に発生するイベント(例: スクロールやリサイズ、キーボード入力など)を効率的に処理する必要がある場合があります。その際に役立つテクニックが「Debounce」と「Throttle」です。今回は、それぞれの仕組みと使い方、注意点についてみていきます。
Debounce(デバウンス)は、特定のイベントが短時間で何度も発生した場合に、最後の1回だけを実行する仕組みです。これにより、過剰なイベント処理を抑え、パフォーマンスの改善が期待できます。
- 入力フィールドでのリアルタイム検索。
- ウィンドウサイズ変更に伴うレイアウト調整。
以下は、テキスト入力フィールドでのリアルタイム検索機能をDebounce
で効率化します。
See the Pen debounce by tones (@tonescodedesign) on CodePen.
JavaScript
const debounce = (func, delay) => {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func(...args), delay);
};
};
const handleSearch = (event) => {
const query = event.target.value;
document.getElementById('result').textContent = `検索結果: ${query}`;
};
const debouncedSearch = debounce(handleSearch, 300);
document.getElementById('search').addEventListener('input', debouncedSearch);
HTML
<input type="text" id="search" placeholder="検索キーワードを入力" />
<p id="result"></p>
このコードでは、ユーザーが入力するたびに検索が即座に行われるのではなく、入力が300ms間隔で安定した場合にのみ実行されます。
Throttle
は、特定の処理を一定の間隔でしか実行しない方法です。頻繁な呼び出しがあっても、設定した間隔内で1回だけ処理を実行します。
- スクロールイベントでのパフォーマンス向上。
- ウィンドウリサイズ時のパフォーマンス改善。
以下は、スクロールイベントにThrottleを適用した例です。
func
: 実行したい関数limit
: 処理を実行する間隔(ミリ秒)
- 最初に関数が呼び出されたときはすぐに実行
- その後、指定した
limit
ミリ秒以内に再び呼び出される場合は、次の実行をスケジュール - 最後に実行された時間 (
lastRan
) と現在の時間 (now
) を比較して、次の実行タイミングを調整します
See the Pen throttle by tones (@tonescodedesign) on CodePen.
JavaScript
const progressBar = document.getElementById('progressBar');
// Throttle関数
function throttle(func, limit) {
let lastRan = 0; // 最後に実行した時刻を保持
let timeout; // 次の実行をスケジュールするタイマー
return function (...args) {
const context = this;
const now = Date.now();
if (now - lastRan >= limit) {
// 前回の実行から `limit` 経過していれば実行
func.apply(context, args);
lastRan = now;
} else {
// 経過していない場合は次の実行をスケジュール
clearTimeout(timeout); // 既存のタイマーをクリア
timeout = setTimeout(() => {
func.apply(context, args);
lastRan = Date.now(); // 実行後にタイムスタンプを更新
}, limit - (now - lastRan)); // 次回の実行までの残り時間を計算
}
};
}
// スクロール処理
function updateProgressBar() {
const scrollTop = window.scrollY;
const docHeight = document.body.scrollHeight - window.innerHeight;
const scrollPercent = (scrollTop / docHeight) * 100;
progressBar.style.width = `${scrollPercent}%`;
}
// Throttleを適用
window.addEventListener('scroll', throttle(updateProgressBar, 100));
HTML
<div class="progress-bar" id="progressBar"></div>
CSS
body {
height: 2000px;
margin: 0;
}
.progress-bar {
position: fixed;
top: 0;
left: 0;
height: 8px;
background: lightblue;
width: 0%;
}
throttle
関数により、スクロールイベントが100msごとに実行されるので、パフォーマンスを維持しながらスムーズな進捗バーが実装できます。
特徴 | Debounce | Throttle |
---|---|---|
処理の頻度 | 最後のイベント後、一定時間が経過して実行 | 一定間隔ごとに処理を実行 |
主な用途 | ユーザー入力の処理、フォームの自動保存など | スクロールやリサイズイベントなど |
Debounce
を使いすぎると、ユーザーが即時応答を期待している場面で遅延を感じることがあります。Throttle
を過剰に使うと、イベントが期待通りに処理されない場合があります。
- 検索ボックスの入力にDebounceを使用する場合、スクリーンリーダーで適切にフィードバックを提供するようにしましょう。ユーザーが入力結果をリアルタイムで確認できるように、必要に応じて「検索ボタン」を補完的に配置します。
HTML
<label for="search">検索:</label>
<input type="text" id="search" aria-describedby="search-help" />
<button id="searchButton">検索</button>
<div id="search-help">入力を完了後、数秒お待ちください。</div>