views:

90

answers:

8

I have a page that's very image-intensive. This is by client request - it's a list of merchants with a logo for each one. The list is quite long (over 500), and yes - the client insists on displaying all of them. We do have an ajax typeahead search to help users find what they're looking for without scrolling, so it's not a total disaster.

Here's the issue: the client is just now realizing that it takes a long time to load this page because of all the logos. Even if each one is only a few kb, it still adds up pretty quickly. He's now decided he wants a progress bar to display while the images are loading. I've never done that before, so I starting looking around, and most of the ones I've seen rely on getting an array of img tags and looping through to check the complete property. The problem I'm having (at least I think this is what's causing the problem) is that the image tags are generated by a database query, and I think the javascript to get the image array is loading before the image tags are finished loading. Obviously this isn't an issue on pages where the images are hard-coded.

Can anyone point me in the right direction of how I can implement a progress bar on img tags that get loaded dynamically? My site is written in PHP, so I'm perfectly happy to do something server-side if that would work better.

A: 

CSS Sprites will definitely be a good start in your case. If you got 500 spearate images on one page, then browser will have to start 500 new connections to fetch them, and unfortunately, concurrent connections will be around 20 or something, so... it is not good.

CSS Sprites from css-tricks.com

Michael Mao
Thanks, but I don't think that will work -as I indicated, the image information is stored in a database, not hard-coded into our html. Merchants get added and deleted from the database all the time. We can't maintain a single "image" file that contains all images, because we never know from day to day (or even minute to minute) how many images there will be, and which ones they will be.
EmmyS
@EmmyS: Sorry misinterpreted your question. You may consider multiple subdomains for concurrent image loading. I.e., image1.yourdomain.com, image2.domain.com, etc... in this case, you may load more images concurrently.
Michael Mao
A: 

I haven't tested this but something like this might work (its jQuery)

<?php // Do your select image stuff from here ?>
<html>
<head></head>
<body>
<script type="text/javascript">
$(document).ready(function () {
    var images = <?php echo json_encode($images); ?>;
    $.each(images, function(i, image) {
        $(new Image()).load(function () {
            $('body').append(this);
            alert('Loaded '+i+' out of '+images.length);
        }).attr('src', image);
    })
});
</script>
Petah
Again, same as the answer regarding sprites. We don't KNOW at any given time what the images are going to be, so how can we hard-code them into an array as you've shown?
EmmyS
@EmmyS well you might want to explain a bit more how you get your images. How do you store them? How do you select them? What format are they in? If they are file name stored in the database simply echo them out to the script (see updated post). Or you could request a list over Ajax. Or what ever.
Petah
This is a joomla site, so the images are stored in an assets directory, with the paths to them stored in the individual merchants' database records. I've never worked with jquery, and am not terribly familiar with ajax. What format would the json_encode need to return? i.e. if my query is select img_path from merchants, what do I need to return via json so that the images var is set correctly? Do I just take the array returned by the query?
EmmyS
@EmmyS, you should run json_encode on an array of file URLs
Petah
A: 

I'd suggest pre-making and caching of the logos (or their various states), as your own hunch is that the main bottleneck is that "the image tags are generated by a database query". Is this at all possible?

It's better to store a few states or whatever with a naming scheme that makes it possible to fetch the right image, than regenerating them on-the-fly each time. Of course, you'll need a proper cache handling mechanism, so it's not like an easy task, but more often than not, your hunch is helping you in the right direction.

If you're able to boil it down to a few static files per logo, you could also consider using a CDN and/or multiple subdomains, as Michael Mao suggests.

norwebian
Nope, not possible. As I said, merchants are added and removed from the database on a daily basis. We cannot cache the image tags in any way - they always have to be fetched from the database at the time of request to ensure that the user is getting the correct data.
EmmyS
A: 

Since you already have a javascript search to get people to specific listings faster, how about loading a placeholder static image for all logos and then replacing the placeholder with the correct logos on an as-needed basis? "As-needed" could be determined by JavasScript calculation of window position and any typed input.

Do your just-in-time image loading from multiple subdomain masks to parallelize requests and you should be able to pop the images up somewhat quickly as-needed without bogging down the page by loading unnecessary images.

It's not pretty, but neither is the client's request.

Edit: As far as a progress bar goes, when you determine a window location (or typed-input location), determine how many listings will be in-view, and how many listings to buffer above and below the view. Then you'll have a total number of listings and you can update a JavaScript/HTML progressbar as you dynamically replace the logos within that range.

J. Farray
A: 

It would be much easier to answer the question if I could see the complete code but I can remember doing something remotely similar and I ended up using a custom JavaScript object. You could perhaps start with an object like this somewhere in the head of your app:

function Iterator() {

  var counter = 0;  
  this.images = 215; // This number comes from the DB and gives you a total number of images
  this.progress = counter / this.images

  this.hasMore = function() { counter < this.images }

  this.getPicture = function() {
    // send a request to the server using counter as a parameter
    // upon receiving this request the server should load only ONE image (LIMIT=1 in SQL)
    // with OFFSET equal to the value of "counter"
    // when the image loads (use whatever JS framework you need for that), 
    // we increment the counter:
    counter++;
  }

  this.loadPictures = function() {
    while this.hasMore() {
      this.getPicture()
      // You can do something with the "progress" attribute here, to be visualizad by your progress bar
    }
  }

);

You sure need to instantiate the Iterator object on body load and have it execute the "loadPictures" function.

Please let me know if you have any problems implementing that.

Marcin Wyszynski