EN/CH Mode
BRUCE_FE JS Interview Notes - Implement Debounce Function for Input Events
Learn how to implement debounce functions to optimize form input, search suggestions, and button click events, improving user experience and frontend performance.
EN/CH Mode
Lazy to read articles? Then watch videos!
What is Debounce?
Debounce is a frontend optimization technique used to control the frequency of function triggers, especially suitable for handling rapid consecutive user operations such as typing in input fields, window resizing, scroll events, etc.
Core principle: Delay function execution, and if triggered multiple times within a specified time, use the last operation as the standard.
Debounce Application Scenarios
- 1. Search input suggestions (send requests only after user stops typing)
- 2. Form validation (validate after user finishes input)
- 3. Recalculation when window is resized
- 4. Prevent duplicate button click submissions
- 5. Real-time article draft saving
Basic Implementation: Debounce Function
Below is a simple debounce function implementation that takes a function and delay time as parameters:
function debounce(func, delay = 300) {
let timer = null;
return function(...args) {
// Save this context
const context = this;
// Clear previous timer
clearTimeout(timer);
// Set new timer
timer = setTimeout(() => {
func.apply(context, args);
}, delay);
};
}
Usage Example: Search Input Box
// Define a search function
function search(query) {
console.log("Search:", query);
// Usually there would be an API request here
}
// Create debounced version of search function (500ms delay)
const debouncedSearch = debounce(search, 500);
// Bind to input event
document.querySelector('#search-input').addEventListener('input', function(e) {
debouncedSearch(e.target.value);
});
Advanced Implementation: Debounce with Immediate Execution Option
Sometimes we need the first trigger to execute immediately, then apply debounce afterward, for example: when validating forms, we want immediate feedback for the first input.
function advancedDebounce(func, delay = 300, immediate = false) {
let timer = null;
return function(...args) {
const context = this;
const callNow = immediate && !timer;
clearTimeout(timer);
timer = setTimeout(() => {
timer = null;
if (!immediate) {
func.apply(context, args);
}
}, delay);
// If immediate execution mode and first trigger, call function immediately
if (callNow) {
func.apply(context, args);
}
};
}
Usage Example: Form Validation
// Validate email format
function validateEmail(email) {
const emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,6}$/;
const isValid = emailRegex.test(email);
if (!isValid) {
document.querySelector('#email-error').textContent = 'Please enter a valid email';
} else {
document.querySelector('#email-error').textContent = '';
}
}
// Validate first input immediately, then wait for user to finish typing
const debouncedValidate = advancedDebounce(validateEmail, 500, true);
document.querySelector('#email-input').addEventListener('input', function(e) {
debouncedValidate(e.target.value);
});
Common Issues with Debounce Function Implementation
1. How to Handle this Binding Issues
When using debounce in class components, you need to properly handle this
binding, you can use arrow functions or bind in the constructor.
class SearchComponent extends React.Component {
constructor(props) {
super(props);
this.state = { query: '' };
// Bind this to debounced method
this.debouncedSearch = debounce(this.search.bind(this), 500);
// Or use arrow function directly
// this.search = () => { /* ... */ };
// this.debouncedSearch = debounce(this.search, 500);
}
search() {
// Can safely use this here
console.log('Search:', this.state.query);
}
handleChange = (e) => {
this.setState({ query: e.target.value });
this.debouncedSearch();
}
render() {
return (
<input
type="text"
value={this.state.query}
onChange={this.handleChange}
/>
);
}
}
2. Cancel Debounce Execution
Sometimes we need to cancel delayed execution, for example, cancel pending calls when components unmount:
function debounceWithCancel(func, delay = 300) {
let timer = null;
function debounced(...args) {
const context = this;
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(context, args);
}, delay);
}
// Add cancel method
debounced.cancel = function() {
clearTimeout(timer);
timer = null;
};
return debounced;
}
// Usage in React component
useEffect(() => {
const debouncedFunc = debounceWithCancel(myFunction, 500);
// Register event listener
element.addEventListener('input', debouncedFunc);
// Cleanup
return () => {
element.removeEventListener('input', debouncedFunc);
debouncedFunc.cancel(); // Cancel pending calls
};
}, []);