Build a Live Search Filter in Vanilla JavaScript
Interactive UI features like live search provide real-time feedback and deliver a dynamic user experience. In this hands-on tutorial, we’ll walk through how to implement a live search filter using plain JavaScript—no frameworks, just efficient DOM manipulation and event handling. This technique is perfect for filtering lists, menus, or datasets client-side in web apps.
1. Setup the HTML Structure
We’ll begin with a simple HTML list and a search input field. This basic layout provides the UI for our filtering logic.
<input type="text" id="search" placeholder="Search items..." />
<ul id="item-list">
<li>Apple</li>
<li>Banana</li>
<li>Orange</li>
<li>Grapes</li>
<li>Watermelon</li>
<li>Pineapple</li>
</ul>
Here, we’ve created a simple unordered list (<ul>) of items and an input field users will type into. We’ll add styles later, but for now, the focus is function.
2. Access DOM Elements with JavaScript
Next, we’ll write a script that gets references to the input field and list items. This will allow us to respond to user interactions and control visibility of list items dynamically.
const searchInput = document.getElementById('search');
const listItems = document.querySelectorAll('#item-list li');
We’re using getElementById and querySelectorAll to get direct references to the elements we want to manipulate—standard practice in vanilla JavaScript for performance and simplicity.
3. Add Event Listener for Real-Time Filtering
Now we’ll hook into the input event of our search field. This event fires every time the user changes the text—perfect for creating a live update effect.
searchInput.addEventListener('input', function (e) {
const searchTerm = e.target.value.toLowerCase();
listItems.forEach(function (item) {
const text = item.textContent.toLowerCase();
if (text.includes(searchTerm)) {
item.style.display = '';
} else {
item.style.display = 'none';
}
});
});
This code listens to user input, converts both the search term and item text to lowercase for case-insensitive comparisons, and toggles each item’s display style accordingly. This is simple but effective filtering logic.
4. Debounce Input Events for Performance
If your list contains hundreds or thousands of items, immediate filtering on every keystroke can be inefficient. A debounce function helps by limiting how often the filtering logic runs.
function debounce(func, delay) {
let timer;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => func.apply(this, args), delay);
};
}
const filterItems = function (e) {
const searchTerm = e.target.value.toLowerCase();
listItems.forEach(function (item) {
const text = item.textContent.toLowerCase();
item.style.display = text.includes(searchTerm) ? '' : 'none';
});
};
searchInput.addEventListener('input', debounce(filterItems, 300));
Here, we wrap our filtering function in a debounce helper that introduces a 300ms delay after the user stops typing. This improves performance and UX in large datasets.
5. Enhance Accessibility and User Experience
To make our search experience more accessible and polished:
- Add ARIA labels for screen readers.
- Highlight matching text using
innerHTML. - Add a “No Results” message for empty outcomes.
Example: Highlighting matched text:
function highlightMatch(item, term) {
const regex = new RegExp(`(${term})`, 'gi');
item.innerHTML = item.textContent.replace(regex, '$1');
}
const filterItems = function (e) {
const searchTerm = e.target.value.toLowerCase();
let hasResults = false;
listItems.forEach(function (item) {
const text = item.textContent.toLowerCase();
if (text.includes(searchTerm)) {
item.style.display = '';
highlightMatch(item, searchTerm);
hasResults = true;
} else {
item.style.display = 'none';
}
});
// Optional: Show/hide a “No results” message here
};
Using <mark> tags improves visibility and provides semantic meaning to the highlighted text for assistive technologies.
Conclusion
We’ve built a clean, fast, and accessible live search filter using only vanilla JavaScript. By taking control of DOM manipulation, event handling, and performance optimization, we created a highly responsive feature that runs in any modern browser—with no dependencies.
This kind of dynamic UI element is great for dashboards, admin panels, or any product list view. Mastering these foundational JavaScript interactions will help you build more interactive and performant web applications.
Useful links:


