In-Page Filtered Search With Vanilla JavaScript
Publikováno: 26.10.2021
If you have a page that includes a lot of information, it’s a good idea to let users search for what they might be looking for. I’m not talking about searching a database or even searching JSON data — I’m …
The post In-Page Filtered Search With Vanilla JavaScript appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.
If you have a page that includes a lot of information, it’s a good idea to let users search for what they might be looking for. I’m not talking about searching a database or even searching JSON data — I’m talking about literally searching text on a single rendered web page. Users can already use the built-in browser search for this, but we can augment that by offering our own search functionality that filters down the page making matching results easier to find and read.
Here’s a live demo of what we’re going to build:
I use this same technique on my real project: https://freestuff.dev/.
Meet JavaScript!
Well, you might know JavaScript already. JavaScript is going to handle all the interactivity in this journey. It’s going to…
- find all the content we want to search through,
- watch what a user types in the search input,
- filter the
innerText
of the searchable elements, - test if the text includes the search term (
.includes()
is the heavy lifter here!), and - toggle the visibility of the (parent) elements, depending on if they include the search term or not.
Alright, we have our requirements! Let’s start working.
The basic markup
Let’s assume we have a FAQ page. Each question is a “card” which has a title and content:
<h1>FAQ Section</h1>
<div class="cards">
<h3>Who are we</h3>
<p>It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularized </p>
</div>
<div class="cards">
<h3>What we do</h3>
<p>It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularized </p>
</div>
<div class="cards">
<h3>Why work here</h3>
<p>It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularized</p>
</div>
<div class="cards">
<h3>Learn more</h3>
<p>Want to learn more about us?</p>
</div>
Imagine there are a lot of questions on this page.
To get ready for the interactivity, we’ll have this one line of CSS. This gives us a class we can add/remove depending on the search situation when we get to the JavaScript:
.is-hidden { display: none; }
Let’s add a search input with an event that fires when it is interacted with:
<label for="searchbox">Search</label>
<input
type="search"
oninput="liveSearch()"
id="searchbox"
>
The JavaScript baseline
And here’s the JavaScript that does everything else!
function liveSearch() {
// Locate the card elements
let cards = document.querySelectorAll('.cards')
// Locate the search input
let search_query = document.getElementById("searchbox").value;
// Loop through the cards
for (var i = 0; i < cards.length; i++) {
// If the text is within the card...
if(cards[i].innerText.toLowerCase()
// ...and the text matches the search query...
.includes(search_query.toLowerCase())) {
// ...remove the `.is-hidden` class.
cards[i].classList.remove("is-hidden");
} else {
// Otherwise, add the class.
cards[i].classList.add("is-hidden");
}
}
}
You can probably go line-by-line there and reason out what it is doing. It finds all the cards and the input and saves references to them. When a search event fires, it loops through all the cards, determines if the text is within the card or not. It the text in the card matches the search query, the .is-hidden
class is removed to show the card; if not, the class is there and the card remains hidden.
Here is the link to the demo again.
Adding a delay
To make sure our JavaScript doesn’t run too much (meaning it would slow down the page), we will run our liveSearch
function only after waiting an “X” number of seconds.
<!-- Delete on Input event on this input -->
<label for="searchbox">Search</label>
<input type="search" id="searchbox">
// A little delay
let typingTimer;
let typeInterval = 500; // Half a second
let searchInput = document.getElementById('searchbox');
searchInput.addEventListener('keyup', () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(liveSearch, typeInterval);
});
What about fuzzy searches?
Let’s say you want to search by text that is not visible to user. The idea is sort of like a fuzzy search, where related keywords return the same result as an exact match. This helps expand the number of cards that might “match” a search query.
There are two ways to do this. The first is using a hidden element, like a span, that contains keywords:
<div class="cards">
<h3>Who are we</h3>
<p>It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularized</p>
<!-- Put any keywords here -->
<span class="is-hidden">secret</span>
</div>
Then we need to update our liveSearch function. Instead of using .inner
Text we will use .textContent
to includes hidden elements. See more detail about the difference between innerText and textContent here
for (var i = 0; i < cards.length; i++) {
if(cards[i].textContent.toLowerCase()
.includes(search_query.toLowerCase())) {
cards[i].classList.remove("is-hidden");
} else {
cards[i].classList.add("is-hidden");
}
}
Try typing “secret” on a search box, it should reveal this card, even though “secret” isn’t a displayed on the page.
A second approach is searching through an attribute. Let’s say we have a gallery of images. We can put the keywords directly on the alt
attribute of the image. Try typing “kitten” or “human” in the next demo. Those queries are matching what’s contained in the image alt
text.
For this to work, we need to change innerText
to getAttribute('alt')
since we want to look through alt
attributes in addition to what’s actually visible on the page.
for (var i = 0; i < cards.length; i++) {
if(cards[i].getAttribute('alt').toLowerCase()
.includes(search_query.toLowerCase())) {
cards[i].classList.remove("is-hidden");
} else {
cards[i].classList.add("is-hidden");
}
}
Depending on your needs, you could put your keywords in another attribute, or perhaps a custom one.
Caveat
Again, this isn’t a search technology that works by querying a database or other data source. It works only if you have all the searchable content in the DOM on that page, already rendered.
So, yeah, there’s that. Just something to keep in mind.
Wrapping up
Obviously, I really like this technique, enough to use it on a production site. But how else might you use something like this? An FAQ page is a clear candidate, as we saw, but any situation that calls for filtering any sort of content is fit for this sort of thing. Even a gallery of images could work, using the hidden input trick to search through the alt
tag content of the images.
Whatever the case, I hope you find this helpful. I was surprised that we can get a decently robust search solution with a few lines of vanilla JavaScript.
Have you used this technique before, or something like it? What was your use case?
The post In-Page Filtered Search With Vanilla JavaScript appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.