Ben
Ben

Reputation: 9001

Faster/more efficient Live Search with jQuery?

I have a list of divs which have various data in a text input boxes.

I have a working "Live Search" where, as a user types information into an input box, the divs are filtered until only matching ones are shown or none at all.

However, when there are lots of rows, the responsiveness becomes sluggish and typing in the search terms is slow.

Is there a faster or more efficient method?

Code

$('input#create-row-name').keyup(function(e){
    if($(this).val()!==''){
        liveSearch();
    } else $('div#rows>div.column>div.row').show();
});

function liveSearch(){
    var searchTerm = $('input#create-row-name').val().toString().toLowerCase();
    $('div#rows>div.column>div.row').each(function(){
        var searchResult = $(this).find('div.input-group>input.row-name').val().toString().toLowerCase();
        if(searchResult.indexOf(searchTerm)>=0){
            $(this).show();
        } else $(this).hide();
    })
}

Upvotes: 1

Views: 821

Answers (4)

Tomalak
Tomalak

Reputation: 338158

Cache values. Remove jQuery in the critical sections.

var $rows = $('div#rows>div.column>div.row');
var texts = $rows.map(function () {
    return $(this).find("div.input-group > input.row-name").text().toLowerCase();
}).toArray();

$('input#create-row-name').on("keyup paste drop", function () {
    var searchTerm = $.trim( this.value.toLowerCase() ), 
        lastTerm = $(this).data("lastTerm"),
        i, found;

    if (searchTerm === lastTerm) return;
    $(this).data("lastTerm", searchTerm);

    for (i = texts.length - 1; i >= 0; i--) {
        found = searchTerm === '' || texts[i].indexOf(searchTerm) > -1;
        $rows[i].style.display = found ? "" : "none";
    }
});

It is absolutely sensible to make the search wait until the user has finished typing instead of running it immediately after every keyup event. I recommend a debounce function like the one Sugar.js provides.

Also (as a general tip, not so relevant here due to caching): Keep in mind that overly specific selectors are less efficient. div#rows > div.column > div.row is computationally more expensive than - but probably equivalent to - #rows .row.

Upvotes: 4

Riddler
Riddler

Reputation: 576

Making only small JQuery based alterations this is what i arrived at.

$('input#create-row-name').keyup(function(e){
    var text=$(this).val().toString().toLowerCase();
    if(text!==''){
        liveSearch(text);
    } else $('div#rows>div.column>div.row').show();
});

function liveSearch(searchTerm){   
    $('#rows input.row-name').each(function(){
        var searchResult = $(this).val().toString().toLowerCase();
        var $divHide=$(this).closest("div.row");
        if(searchResult.indexOf(searchTerm)>=0){
            $divHide.show();
        } else $divHide.hide();
    })
}

The alterations i have made are

  • Reuse the textbox value between function defention.
  • Optimised selectors to reduce specificity
  • Directly Looping through all inputs to find values and then finding and hiding corresponding parents instead of looping through Divs and scanning for values in them
  • Could say i have cached values in places

Other better answers appeared as i was typing this one, i would suggest them over mine. I am just trying to submit my solution as well.

Upvotes: 0

freefaller
freefaller

Reputation: 19953

Rather than go through the list on every single key-press, put a tiny delay in there so that if somebody types multiple character in quick succession it doesn't do it every time. (This is a trick that somebody told me about years ago.)

So, for example, something like this (untested)...

var keyupTimer = -1;
$('input#create-row-name').keyup(function(e){
    if ($(this).val()!==''){
        if(keyupTimer != -1)
            window.clearTimeout(keyupTimer);
        keyupTimer = window.setTimeout(function(){
            liveSearch();
            keyupTimer = -1;
        },200);
    } else $('div#rows>div.column>div.row').show();
});

This will run the liveSearch after 1/5th of a second, enough to allow a normal typist to press a few keys before your code does its work.


Update

As commented by @Halcyon, the above is fine if the code that is being run in the timer is already optimised... and as he also pointed out, your code is not.

Rather than try and write something new, I would advise you first use the answer provided by @Tomalak and @Halcyon... and if you still need improvments, try using my suggestion.

Upvotes: 2

Halcyon
Halcyon

Reputation: 57709

jQuery lookups are notoriously slow. See if you can optimize them. You can build an index like:

(function () {
    var index = new WeakMap();

    $('input#create-row-name').keyup(function(e){
        if($(this).val()!==''){
            liveSearch();
        } else $('div#rows>div.column>div.row').show();
    });

    // build index once initially
    $('div#rows>div.column>div.row').each(function(i, o){
        index.set(o, $(this).find('div.input-group>input.row-name'));
        // maybe even cache `.val().toString().toLowerCase()`?
    })

    function liveSearch(){
        var searchTerm = $('input#create-row-name').val().toString().toLowerCase();
        $('div#rows>div.column>div.row').each(function(i, o){
            // use index here
            var searchResult = index.get(o).val().toString().toLowerCase();
            if(searchResult.indexOf(searchTerm)>=0){
                $(this).show();
            } else $(this).hide();
        })
    }
}());

This should significantly speed up your code.

Upvotes: 1

Related Questions