snippetjavascriptMinor
Better way to filter select list
Viewed 0 times
waybetterfilterselectlist
Problem
I'm brand new to JavaScript and jQuery and I feel like there's probably a better way to do this. It seems that the way I designed it may not be very optimal once you get a thousand entries in the list but I do like the fact that it's a live search.
Note: I tried changing the style of the `
http://jsfiddle.net/x6cfF/3/
Note: I tried changing the style of the `
's to display:none` but that doesn't work in all browsers.http://jsfiddle.net/x6cfF/3/
test
test1
test2
test3
window.onload=function() {
var $options = $('#CustomerSelect option');
document.getElementById("SearchBox").onkeyup = function () {
var $HiddenOptions = $('#CustomerSelectHidden option');
$HiddenOptions.each(function (index, value) {
document.getElementById('CustomerSelect').appendChild(this);
});
var search = $.trim($(this).val()).replace(/ +/g, ' ').toLowerCase();
var element = $options.filter(function () {
var text = $(this).text().replace(/\s+/g, ' ').toLowerCase();
return !~text.indexOf(search);
}).appendTo(document.getElementById('CustomerSelectHidden'));
$('#CustomerSelect option').sort(Sort).appendTo('#CustomerSelect');
}
}
function Sort(a, b)
{
return (a.innerHTML > b.innerHTML) ? 1 : -1;
}
Solution
First thing I notice is that you're actually not using jQuery all that much. The point of jQuery is really its convenience compared to the native DOM. You're foregoing that convenience in some places, and relying on jQuery in others. You can of course do it all directly using the DOM and skip jQuery, but you're not quite doing that either. My point is really just that you should decide one way or the other in order to keep the code consistent.
Also, while it's not enforced, convention dictates that function names are
However, the
The hidden select element isn't necessary either; you can clone the full option list into memory, and filter it from there without having to store it in the document.
In fact, by keeping a master list, you can "prepare" that list to make it faster to filter later.
Here's a demo of my take and here's the code:
You could do a few more things, but I'll leave that as an exercise:
-
If the search term contains the previous search term, you can filter the customer select directly, instead of filtering the entire "master list". I.e. if the last search was for "foo" and the next is for "foobar", you already have the results that match "foo", and only have to find a further subset that contains "foobar" rather than starting from the beginning.
-
If you have a very long list, or you're retrieving the search results via ajax, it's beneficial to have a delay between searches. I.e. on keyup wait, say, 100ms before filtering the list, to see if the user's still typing.
Also, while it's not enforced, convention dictates that function names are
lowerCamelCase. If you use PascalCase it implies that a function is a constructor. I.e. your Sort function should be just sort.However, the
sort function is not necessary. Provided you always search in a "master list" of options, and those are already sorted, then the filtered options will retain that order.The hidden select element isn't necessary either; you can clone the full option list into memory, and filter it from there without having to store it in the document.
In fact, by keeping a master list, you can "prepare" that list to make it faster to filter later.
Here's a demo of my take and here's the code:
$(function () {
var customerSelect = $("#customer"),
searchField = $("#search"),
options = customerSelect.find("option").clone(); // clone into memory
// generic function to clean text
function sanitize(string) {
return $.trim(string).replace(/\s+/g, ' ').toLowerCase();
}
// prepare the options by storing the
// "searchable" name as data on the element
options.each(function () {
var option = $(this);
option.data( "sanitized", sanitize(option.text()) );
});
// handle keyup
searchField.on("keyup", function (event) {
var term = sanitize( $(this).val() ),
matches;
// just show all options, if there's no search term
if( !term ) {
customerSelect.empty().append(options.clone());
return;
}
// otherwise, show the options that match
matches = options.filter(function () {
return $(this).data("sanitized").indexOf(term) != -1;
}).clone();
customerSelect.empty().append(matches);
});
});You could do a few more things, but I'll leave that as an exercise:
-
If the search term contains the previous search term, you can filter the customer select directly, instead of filtering the entire "master list". I.e. if the last search was for "foo" and the next is for "foobar", you already have the results that match "foo", and only have to find a further subset that contains "foobar" rather than starting from the beginning.
-
If you have a very long list, or you're retrieving the search results via ajax, it's beneficial to have a delay between searches. I.e. on keyup wait, say, 100ms before filtering the list, to see if the user's still typing.
Code Snippets
$(function () {
var customerSelect = $("#customer"),
searchField = $("#search"),
options = customerSelect.find("option").clone(); // clone into memory
// generic function to clean text
function sanitize(string) {
return $.trim(string).replace(/\s+/g, ' ').toLowerCase();
}
// prepare the options by storing the
// "searchable" name as data on the element
options.each(function () {
var option = $(this);
option.data( "sanitized", sanitize(option.text()) );
});
// handle keyup
searchField.on("keyup", function (event) {
var term = sanitize( $(this).val() ),
matches;
// just show all options, if there's no search term
if( !term ) {
customerSelect.empty().append(options.clone());
return;
}
// otherwise, show the options that match
matches = options.filter(function () {
return $(this).data("sanitized").indexOf(term) != -1;
}).clone();
customerSelect.empty().append(matches);
});
});Context
StackExchange Code Review Q#23706, answer score: 6
Revisions (0)
No revisions yet.