views:

59

answers:

3

I'm trying to find an efficient algorithm for dynamically loading background-images for a bunch of <li>'s, and are having some efficiency problems. My current code looks like this:

function elementInView($elem, vps, vpe) {
    var elempos = $elem.position(); 
    var pagestart = elempos.top + vps;
    var pageend = elempos.top + vps + $elem.height();
    var offset = 78 - Math.max(0,vps-pagestart) - Math.max(0,pageend-vpe);    
    // I want to update all items that are within 200 pixels of 
    // the current viewport
    return (vpe > 0 && offset > -200); 
}

$('#container').bind('scroll', function() {
    var $container = $(this);
    var vps = $container.scrollTop();
    var vpe = vps + $container.height();

    $('li:not(.bgset)', '#container').each(function() {
        var $this = $(this);
        if (elementInView($this,vps,vpe)) {
            $this.addClass('.bgset');
            // Set the background-image (doesn't affect performance noticably.)
        }
    });
});

This takes between 200-600 ms on a list with ~250 items, and ~1500 ms for a list with ~1500 items, which makes it pretty much unusable.

Can anyone see any problems with this, or is this basically the best way to do this? I've tried getting ALL <li>'s instead of $('li:not(.bgset)', but that didn't really have any impact at all.

(The list contains dynamically generated background-images (which doesn't get cached AND get's changed quite a lot), and loading around ~1500 of them really slowed everything down, which is why I tried this approach)

EDIT: Forgot to mention, I've thought about writing a custom jQuery-selector which matches the elements that return true for elementInView, would this be a better approach?

A: 

Sounds like your app does a lot of reflow and repaints which is a major perfomance hit as well as bad for user experience nevermind the huge amount of HTTP traffic.

Have you thought about maybe spriting on the on the fly with the relevent CSS being generated at the same time. This way you could hopefully cut HTTP requests down and cut the amount of repaint/reflow down.

AutomatedTester
Spriting on the fly isn't really an option, since the different background needs to be regenerated quite often, which would mean that the entire sprite would need to be generated.. thanks though.
Per Holmäng
A: 

The DOM is slow. To get raw speed, you need to use innerHTML.
To avoid string manipulations, you may consider a JS templating engine, there are plenty of them.
We've built the templating engine PURE, as it is tiny and fast, it works very well to build pages for mobiles.

Another option could be to consider the problem upstream. Are you sure you want to provide your user a list of hundreds/thousands of items?

Mic
A: 

First of all I recommend using a profiler to get real knowledge of what's slowing your code down. There're profilers in firefox (firebug), chrome and explorer. But I can think of a few problems right now.

I suppose that all LIs are children of a same UL, so you can access them directly (firstChild/nextSibling) without calling $(). Even if there're more than one UL, direct access is still faster then $().

Another possible reason is complex CSS. I've seen a case where because of too much floats and positioning single access to element's offsetTop took half a second. This might influence speed of .height() and .position().

But profile first!