Reputation: 1322
I have this simple jQuery call to highlight a bunch of elements (couple thousand):
jQuery(elem).addClass('highlighted');
What the browser does is it quickly adds the class, but does not repaint the element. Is there a way to render each "highlighted" element as soon as i add the class. Right now the user sees a stutter until my loop of adding the class have finished.
More code:
var elem = findNext(); // walks the dom and finds next search match. ~10ms each call
while(elem){
highlight(elem);
elem = findNext();
}
function highlight(elem){
jQuery(elem).addClass('highlight');
...
}
Upvotes: 1
Views: 1992
Reputation: 3951
Check out solution using only jQuery 1.4.2 (without setTimeout) It uses queue on $(document) to postpone function execution and execute them in order.
$(function() {
$('.highlight').each(function() {
var e = $(this);
$(document).delay(1000).queue(function(next) {
e.addClass('a');
next();
});
});
});
Upvotes: 0
Reputation: 338316
This is because DOM changes are not rendered as long as a JavaScript function is running. User interface code is single-threaded and a browser locks while executing it. Normally this is not problem because JS is quite fast and functions do not run very long. But if they do, you see sluggish behavior as a result.
Your function needs to stop in the middle of its work to give the browser a chance to become responsive again. You can solve this by using setTimeout()
and remembering where you left off.
This should give you an idea:
// prepares a closure function to handle affected elements in chunks of n
function updatePartial(elems, chunksize) {
var current = 0;
return function() {
// changes n elements in a loop
for (var i=0; i<chunksize; i++) {
jQuery(elems[current+i]).addClass('highlighted');
}
current += chunksize;
// calls itself again after a short break
if (current < elems.length) setTimeout(arguments.callee, 10);
}
}
// aquire and execute the closure function
updatePartial(jQuery("selector").get(), 100)();
(Tested at http://jsfiddle.net/fPdAg/)
Closures are an elegant way of avoiding global variables you would need in other implementations.
EDIT: A generalized version of the above would be this:
// prepares a closure function to handle affected elements in chunks of n
function updatePartial(elems, chunksize, payload) {
var current = 0;
return function() {
// changes n elements in a loop
for (var i=0; i<chunksize; i++) {
// apply the payload function to current element
payload.apply(elems[current+i]);
}
current += chunksize;
// calls itself again after a short break
if (current < elems.length) setTimeout(arguments.callee, 10);
}
}
// aquire and execute the closure function, supply custom payload function
updatePartial(jQuery("selector").get(), 100, function() {
jQuery(this).addClass('highlighted');
})();
Upvotes: 3
Reputation: 5102
I think the reason is because the browser behavior, not the JQuery problem. When call
jQuery(elem).addClass('highlighted');
While Javascript runnig, the browser stop working until JS finish its execution. So browser will not repain anything until JQuery addClass to every element.
Let try this trick
<script>
x = jQuery(elem).size();
y = number_item_each_time;
z = time_by_micro;
start = 0;
t = setInterval(function(){
$(elem).slice(start, start + y).addClass('highlighted');
start = start + y;
if(start > x)
clearInterval(t);
}, z);
</script>
Upvotes: 1
Reputation: 7871
Are you sure the browser is quickly adding the class? It sounds like it takes a noticeable amount of time to complete the operation, which is what I'd expect.
You could try batching the addClass() calls into a series of setTimeout callbacks (e.g. 10 calls that each do 100 elements) to show the result more quickly.
Update
I just noticed that you said "until my loop of adding the class have finished". Are you making a single call to addClass, or calling it individually for each element inside a for loop?
You should definitely be making a single call a la jQuery('.all-your-elements').addClass('highlighted')
. It will be more readable and much faster.
Upvotes: 0