views:

707

answers:

6

As the user pans around a Google Map, a list of of currently visible markers is updated. This list contains up to 1000 items, and it slows down when several hundred li's are shown or hidden all at once. It's less than half a second, but it's getting annoying.

An array (newLiList) contains the items which should now be visible. Another array (currentLiList) has the items which were previously visible. Both arrays contain the ids of the li's as indexes.

for (var i in newLiList) {
    if (currentLiList[i] != true) {
     $("ul#theList li#"+i).show();
    }
}
for (var i in currentLiList) {
    if (newLiList[i] != true) {
     $("ul#theList li#"+i).hide();
    }
}

Is there a faster way to do this?

+3  A: 

You should try caching your selector and using context:

var targetList = $("ul#theList");

and replace:

$("ul#theList li#"+i).show();

with

$("#"+i, targetList).show();

That'll reduce the amount of times jquery has to bounce around the entire DOM.

Mike Robinson
+5  A: 

You could store the <li> elements in an array directly so you don't have to do hundreds of CSS selector lookups. Then instead of $("ul#theList li#"+i) you just do liArray[i].

FYI, "ul#theList li#<i>" is equivalent to just "#<i>" since element IDs are (supposed to be) unique. You don't need the extra context.

John Kugelman
I did know that those selectors were equivalent, but I didn't know that simplifying it would have as big of an effect as it did (3.5x). However, storing the <li> elements in an array was even faster (4.5x). Thanks!
Chris B
It's not entirely true that li#id and #id is the same. You will get the same object, but the speed will not be the same, but it depends a bit on the DOM. But generally if you traverse a DOM of mixed elements adding the li, div, or what type of element you want to search for will fasten the search by a few ms.
googletorp
@googletorp - I meant that li#id and #id would return the same object. And adding the "li" to "#id" didn't speed up the search - it slowed it down by about 2x.
Chris B
@Chris B: yea, just #id uses document.getElementById (very fast), li#id finds all li's, then filters by id attribute.
seanmonstar
A: 

You could try hiding the list while you reset all the list items. It might prevent some redraws.

Also, consider just using

$("ul#theList li#"+i).attr("display","none");

$("ul#theList li#"+i).attr("display","block");

rather than hide() and show() since they can take a speed parameter for fancy animation. I don't know if they are any slower if you don't use it, but it's worth checking.

Gareth Simpson
Hiding the list did help a little bit, but changing the jquery to $().css("display","none"); didn't make much difference.
Chris B
A: 

Most likely this is slow because you are asking jQuery to traverse the DOM several hundred times. If you instead made a string with all the Ids seperated by comma you could select and hide/show them all in one action.

Let me clarify a bit what I meant, since if you just do $(string_of_elements) jQuery will of cause traverse the entire DOM for each element.

$("ul#theList").find(string_of_elements).show()

Using this syntax, jQuery will first find the ul element and then only have to traverse it to find the li elements. Now if you have both lists converted to a string, you can do some really neat stuff:

$("ul#theList")
    .find(string_of_elements_to_show).show().end()
    .find(string_of_elements_to_hide).hide();

Using this syntax and chaining the two events has another advantage. The actual showing and hiding will happen all at once, rather than it being a one by one hiding and showing.

googletorp
This was quite slow - it still has to traverse the DOM to find each element, even though they're all specified in one statement.
Chris B
+2  A: 

For every single .show() and .hide() function call, you're causing a redraw in the browser.

Setting the whole list to display none before you change the list items will prevent the browser from having to recalculate the flow of the document every time, and then you could set the list back to block.

Alternatively, if you don't want the flicker of the list disappearing, you could clone the list, make all the items hide that should hide, and then replace the old list with the new.

seanmonstar
A: 

Have you profiled your code?

Never assume which part of the code is running slow, profile the code in FireBug (Assuming you are using Firefox) and tweak the code which is running slower.

SolutionYogi
Yes. This was the code that was running slow.
Chris B