ApaSoft
ApaSoft

Reputation: 43

Fast filter in a list of records with JavaScript

I have a list with about 10 000 customers on a web page and need to be able to search within this list for matching input. It works with some delay and I'm looking for the ways how to improve performance. Here is simplified example of HTML and JavaScript I use:

<input id="filter" type="text" />
<input id="search" type="button" value="Search" />
<div id="customers">
    <div class='customer-wrapper'>
        <div class='customer-info'>
            ...
        </div>
    </div>
    ...
</div>

<script type="text/javascript">
    $(document).ready(function() {
        $("#search").on("click", function() {
            var filter = $("#filter").val().trim().toLowerCase();
            FilterCustomers(filter);
        });
    });

    function FilterCustomers(filter) {
        if (filter == "") {
            $(".customer-wrapper").show();
            return;
        }
        $(".customer-info").each(function() {
            if ($(this).html().toLowerCase().indexOf(filter) >= 0) {
                $(this).parent().show();
            } else {
                $(this).parent().hide();
            }
        });
    }
</script>

The problem is that when I click on Search button, there is a quite long delay until I get list with matched results. Are there some better ways to filter list?

Upvotes: 3

Views: 2782

Answers (3)

rba
rba

Reputation: 358

1) DOM manipulation is usually slow, especially when you're appending new elements. Put all your html into a variable and append it, that results in one DOM operation and is much faster than do it for each element

function LoadCustomers() {
    var count = 10000;
    var customerHtml = "";
    for (var i = 0; i < count; i++) {
        var name = GetRandomName() + " " + GetRandomName();
        customerHtml += "<div class='customer-info'>" + name + "</div>";
    }
    $("#customers").append(customerHtml);
}

2) jQuery.each() is slow, use for loop instead

function FilterCustomers(filter) {
    var customers = $('.customer-info').get();
    var length = customers.length;
    var customer = null;
    var i = 0;
    var applyFilter = false;
    if (filter.length > 0) {
        applyFilter = true;
    }
    for (i; i < length; i++) {
        customer = customers[i];
        if (applyFilter && customer.innerHTML.toLowerCase().indexOf(filter) < 0) {
            $(customer).addClass('hidden');
        } else {
            $(customer).removeClass('hidden');
        }
    }
}

Example: http://jsfiddle.net/29ubpjgk/

Upvotes: 3

ApaSoft
ApaSoft

Reputation: 43

Thanks to all your answers and comments, I've come at least to solution with satisfied results of performance. I've cleaned up redundant wrappers and made grouped showing/hiding of elements in a list instead of doing separately for each element. Here is how filtering looks now:

function FilterCustomers(filter) {
    if (filter == "") {
        $(".customer-info").show();
    } else {
        $(".customer-info").hide();
        $(".customer-info").removeClass("visible");
        $(".customer-info").each(function() {
            if ($(this).html().toLowerCase().indexOf(filter) >= 0) {
                $(this).addClass("visible");
            }
        });
        $(".customer-info.visible").show();
    }
}

And an test example http://jsfiddle.net/vtds899r/

Upvotes: 1

jcbermu
jcbermu

Reputation: 571

The problem is that you are iterating the records, and having 10000 it can be very slow, so my suggestion is to change slightly the structure, so you won't have to iterate:

  1. Define all the css features of the list on customer-wrapper class and make it the parent div of all the list elements.

  2. When your ajax request add an element, create a variable containing the name replacing spaces for underscores, let's call it underscore_name.

  3. Add the name to the list as:

var customerHtml = "<div id='"+underscore_name+'>" + name + "</div>";

Each element of the list will have an unique id that will be "almost" the same as the name, and all the elements of the list will be on the same level under customer-wrapper class.

  1. For the search you can take the user input replace spaces for underscores and put in in a variable, for example searchable_id, and using Jquery:

$('#'+searchable_id).siblings().hide();

siblings will hide the other elements on the same level as searchable_id.

The only problem that it could have is if there is a case of two or more repeated names, because it will try to create two or more divs with the same id.

You can check a simple implementation on http://jsfiddle.net/mqpsppxm/

Upvotes: 0

Related Questions