HTML5 Data Attributes Search using jQuery
First appeared on mobifreaks.com · Updated
I had to search a list of names, entirely on the client and quickly. I first tried Filtrify, an advanced tag filtering plugin built on HTML5 data attributes. It was good, but it generated a dropdown for picking movie genres, and my list wasn't about movies. I wanted a plain text field that hides every item that doesn't match. So I started digging.
The 2012 version
<input type="text" id="input" onfocus="this.value = '';" value="Start Searching">
<ul>
<li data-label="ant">ant</li>
<li data-label="apple">apple</li>
<li data-label="ball">ball</li>
<!-- ...45 words in total -->
</ul>
jQuery(document).ready(function ($) {
$('#input').on('keyup', function () {
var input = $(this).val();
$('ul li').show();
$('ul li').not('[data-label*=' + input + ']').hide();
});
On every key press it showed all items, then hid the ones whose data-label didn't contain the typed text.
What was wrong with it
- The closing
});was missing, so the snippet as published didn't run. - It was case-sensitive. I listed this as a known issue: typing "a" wouldn't match "Ant".
- It broke on spaces and quotes. The typed text went straight into the selector without quotes, so typing two words or an apostrophe caused a selector error.
- The placeholder was a fake value, cleared on focus. Screen readers read "Start Searching" as if it were typed text, and the field had no label.
keyupmissed pasting with the mouse.
The same thing today
<label for="filter">Filter words</label>
<input type="search" id="filter" placeholder="Start typing" autocomplete="off">
<p id="filter-status" aria-live="polite"></p>
<ul id="words">
<li data-label="ant">ant</li>
<li data-label="apple">apple</li>
<li data-label="ball">ball</li>
</ul>
const input = document.getElementById('filter');
const items = [...document.querySelectorAll('#words li')];
const status = document.getElementById('filter-status');
input.addEventListener('input', () => {
const query = input.value.trim().toLowerCase();
let shown = 0;
for (const item of items) {
const match = item.dataset.label.toLowerCase().includes(query);
item.hidden = !match;
if (match) shown++;
}
status.textContent = query ? `${shown} of ${items.length} shown` : '';
});
- No jQuery.
dataset,includes()and thehiddenproperty do the work. - Case-insensitive, and safe with any characters, because nothing is built into a selector.
- The
inputevent fires for typing, pasting and clearing the field. - A real label and placeholder, and a live status message so screen reader users hear how many items match.
For a few hundred items this is instant. For thousands, or for fuzzy matching ("aple" finding "apple"), a small library like Fuse.js is worth it.