HiveBrain v1.2.0
Get Started
← Back to all entries
snippetjavascriptMinor

Better way to filter select list

Submitted by: @import:stackexchange-codereview··
0
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 `'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 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.