See this this question for background. This is a live filter where results are filtered as you type. I've taken into account Kerri's answer and expanded on her recommendation. I've added a message that appears if no results are found, and a display for how many results are being displayed out of the original set. You can test this in the attached snippet.
I'm pretty happy with my work here, but I think the code is too large, and becoming complicated. Perhaps it can benefit from creating an object and splitting out the functionality into smaller methods.
var liveFilter = (function() {
/**
* Initializes a search field with associated list and filters it live while typing
*
* @param {string} searchFieldId An ID of a search field input element
* @param {string} listId An ID of an element containing items that should be filtered
* @param {object} [options] Specifies additional options:
* * selector: A query to be run against list; the results
* will be filtered according to the value of the search field. If
* not specified, the filter matches all direct children.
* * filterClass: A class name to be applied to items that
* are going to be filtered out. If not specified, defaults to
* "filter-hidden".
* * counterElementId: An ID of an element that displays results count.
* * messageElementId: An ID of an element that displays a message when no results are found.
*/
function liveFilter(searchFieldId, listId, options) {
var searchField = document.getElementById(searchFieldId),
list = document.getElementById(listId),
selector,
filterClass;
if (!searchField) {
throw new Error("No search element found with id " + searchFieldId);
}
if (!list) {
throw new Error("No list element found with id " + listId);
}
if (options !== undefined) {
selector = options.selector;
filterClass = options.filterClass;
if (options.counterElementId) {
var counter = document.getElementById(options.counterElementId);
if (!counter) {
throw new Error("No counter element found with id " + options.counterElementId);
}
}
if (options.messageElementId) {
var messageContainer = document.getElementById(options.messageElementId);
if (!messageContainer) {
throw new Error("No counter element found with id " + options.messageElementId);
}
}
}
if (filterClass === undefined) {
filterClass = "filter-hidden";
}
var handler = function searchFieldChanged() {
var text = this.value,
regexp = RegExp(text, 'i'),
nodes = (selector === undefined) ? list.children
: list.querySelectorAll(selector),
filteredNodesCount = 0,
node;
for (var i = 0, nodesLength = nodes.length; i < nodesLength; i++) {
node = nodes[i];
if (node.textContent.search(regexp) < 0) {
filteredNodesCount += 1;
node.classList.add(options.filterClass);
}
else {
node.classList.remove(options.filterClass);
}
// Counter should only be visible if:
// 1. There is text in the search box
// 2. A counter element id was passed in the options
if (counter) {
if (text) {
counter.textContent = (nodesLength - filteredNodesCount) + "/" + nodesLength;
counter.style.display = 'block';
}
else {
counter.style.display = 'none';
}
}
// Message should only be shown if:
// 1. A message element id was passed in the options
// 2. There is text in the search box
// 3. All results get filtered and nothing is found.
// I.e. the number of results filtered is equal to the number of items
if (messageContainer) {
messageContainer.style.display =
(filteredNodesCount === nodesLength) ? 'block' : 'none';
}
}
};
searchField.addEventListener("input", handler, false);
}
return liveFilter;
})();
// Usage
liveFilter('js-users-list-filter', 'js-users-list', {
selector: 'li',
filterClass: 'filter-hidden',
counterElementId: 'js-users-list-filter-counter',
messageElementId: 'js-users-list-filter-message'
});
#js-users-list-filter-message {
display: none;
}
#js-users-list-filter-counter {
display: none;
}
.filter-hidden {
opacity: .1;
}
<input type="text" id="js-users-list-filter" />
<p id="js-users-list-filter-counter"></p>
<div id="js-users-list-filter-message">
<p>No users where found.</p>
</div>
<ol id="js-users-list">
<li>Barney Gumble</li>
<li>Bart Simpson</li>
<li>Waylon Smithers</li>
<li>Lisa Simpson</li>
<li>Montegormey Burns</li>
<li>Clarence Wiggum</li>
<li>Ned Flanders</li>
<li>Mo Szyslak</li>
<li>Apu Nahasapeemapetilon</li>
<li>Marg Simpson</li>
<li>Nelson Muntz</li>
</ol>
AngularJS
excel. – Dmitri Zaitsev Apr 6 '15 at 4:31