Reputation: 9001
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
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
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
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
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
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