loading images to browser cache before slideshow

Hi,

I looking for some help on a bit of bespoke scripting to source a lot of images into batch slideshows.

The images don't sit locally as they're all from different sources/CDNs etc and jpegs

Is there a way in jquery to load image 1 and when the user hovers over it, a little animated 'loading' gif is overlayed until it has fetched the other images in the pool before it starts rotating through them at x speed?

Also, if an image doesn't load for some reason, can this be skipped?

So:

- page loads first image on page load
- upon hover, the script attempts to load the others
- once all images have either loaded or failed to load;
- rotate through the loaded ones at x speed (ignoring non-loaded images)


Huge Thanks for your help
dolythgoeAsked:
Who is Participating?
 
Michel PlungjanIT ExpertCommented:
Euuw - lack of sleep last night.

Here
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
<script type="text/javascript">
var caches = {};
function addImages(container) {
  var images = caches[container].imgArr;
  var cont = $("#"+container);
  cont.empty();
  for (var i=0, n=images.length;i<n;i++) {
    cont.append('<img src="'+images[i].src+'"/>')
  }
  // here you can start the slideshow for this container 
}
(function($) {

  // Argument 0 is the container, Argument1 is an array of image paths relative to the current page.
  $.preloadImages = function() {
    var container = arguments[0];
    var cache = caches[container] = {};   
    cache.imgArr  = []; // holds only images that loaded
    cache.stack = arguments[1]; // all image urls
    cache.counter = 0; // joint errors and loaded
    cache.errors  = 0; // only errors
    cache.loaded  = 0; // only loaded - same as .imgArr.length
    for (var i = cache.stack.length; i--;) {
      var cacheImage = $('<img/>');
      cacheImage.bind("load",function() { cache.imgArr.push(this); cache.loaded++; cache.counter++;if (cache.counter>=cache.stack.length) addImages(container) })
      cacheImage.bind("error",function() { cache.errors++; cache.counter++; if (cache.counter>=cache.stack.length) addImages(container)  })
      cacheImage.attr("src",cache.stack[i]);
    }
    $("#"+container).unbind("mouseenter"); // remove the mouseOver we defined in the onload
  }
})(jQuery);

$(document).ready(function() {
  $(".slideshow").mouseenter(function() {
		var dataString=$(this).attr("data-string"); 
		var thisId = this.id; 
		//Set idx to that of the div
		var idx = thisId.replace("div","");
		//Call the php script for images
		$.ajax({
			type: "POST",
			url: "getimages.php",
			data: dataString,
			cache: true,
			success: function(imgdata) {
        // add the stack to the caches[divId], preloads and loads the div with the result			
			  jQuery.preloadImages(thisId,imgdata.split(",")); 
			}
		});
  });    
});
</script>  
</head>
<body>
<div id="div0" class="slideshow" data-string="var1=blah&var2=blah&var3=blah">
  <img src="http://www.amnh.org/exhibitions/frogs/images/home/frogs_image.jpg" />
 
</div>  
</body>
</html>

Open in new window

0
 
Michel PlungjanIT ExpertCommented:
Sounds to me we can start with

http://malsup.com/jquery/cycle/

you can add

$("#slideshow img").error(function() { $(this).remove()});

which will remove all images that cannot be found (possibly ending up with an empty slideshow)
0
 
dolythgoeAuthor Commented:
Hey, thanks for that.

I've got quite a few of these image 'stacks' on the page and what I'm most concerned about is the load order.

e.g. 10 stacks of 10 images - I would want to load:

The 1st image in each stack, the rest of the page.

Then upon hover over, request the other images in the stack (check for errors etc like you gave an example of above) and start rotating through.

Can this script do something like this?

Cheers
0
Ultimate Tool Kit for Technology Solution Provider

Broken down into practical pointers and step-by-step instructions, the IT Service Excellence Tool Kit delivers expert advice for technology solution providers. Get your free copy now.

 
Michel PlungjanIT ExpertCommented:
Very likely.
What you are asking seems not really "can it do it" but more like "do it for me" which will mean (for me at least)
A) download the script
B) investigate how to lazy load with this particular script
C) create an example and test
D) Present the finished result
E) assist you with YOUR specific implementation since you are bound to have a different layout than I chose.

That is stuff I charge EUR 50 an hour for and being EE expert is unpaid from the good of our hearts.
Can you at least do steps A and C please?
0
 
dolythgoeAuthor Commented:
Hi,

Well I just need some direction as I will piece this together myself. I did mean 'can it do' as it's important to at least understand the capabilities before experimenting.

My approach now is as follows:

- upon hover, request additonal images via ajax to a php file
- php file will echo back the images in the stack
- the jquery part will then take these urls and rotate through them

The ajax/php part will at least take care of the image 404 bit and sequential loading issue.

The jquery part is then just about transitional effects once it has loaded the images.

I've done the php file, now to the ajax hover calls :)

0
 
Michel PlungjanIT ExpertCommented:
Here is a start.


<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
<script type="text/javascript">
var caches = {};
function addImages(container) {
  var images = caches[container].imgArr;
  var cont = $("#"+container);
  cont.empty();
  for (var i=0, n=images.length;i<n;i++) {
    cont.append('<img src="'+images[i].src+'"/>')
  }
  // here you can start the slideshow for this container 
}
(function($) {

  // Argument 0 is the container, Argument1 is an array of image paths relative to the current page.
  $.preloadImages = function() {
    var container = arguments[0];
    var imageArr = arguments[1];
    var cache = caches[container] = {};   
    caches[container].imgArr  = [];
    caches[container].counter = 0;
    caches[container].errors  = 0;
    caches[container].loaded  = 0;
    for (var i = imageArr.length; i--;) {
      var cacheImage = $('<img/>');
      cacheImage.bind("load",function() { cache.imgArr.push(this); cache.loaded++; cache.counter++;if (cache.counter>=cache.imgArr.length) addImages(container) })
      cacheImage.bind("error",function() { cache.errors++; cache.counter++; if (cache.counter>=cache.imgArr.length) addImages(container)  })
      cacheImage.attr("src",imageArr[i]);
    }
    $(".slideshow").unbind("mouseenter"); // remove the mouseOver we defined in the onload
  }
})(jQuery);

$(document).ready(function() {
  // the stacks match the number of the div - div0 has stack 0 - this can be ajaxed or just present in the code. It is only text
  var stacks = [
   ["http://www.amnh.org/exhibitions/frogs/images/home/frogs_image.jpg","http://static.howstuffworks.com/gif/frog-1.jpg"]
  ];

  $(".slideshow").mouseenter(function() {
    var idx = this.id.replace("div","");
    jQuery.preloadImages(this.id,stacks[idx])
  });    

});
</script>  
</head>
<body>
<div id="div0" class="slideshow">
  <img src="http://www.amnh.org/exhibitions/frogs/images/home/frogs_image.jpg" />
</div>  
</body>
</html>

Open in new window

0
 
dolythgoeAuthor Commented:
Wow! Huge thanks for that - just running through it trying to dicipher ;)

So looking at this..var stacks is the place where I would output the sets of images in order to match the div id's?

I've been experimenting with the ajax part - well, psuedo coding if you like, what do you think of the approach below? Syntax is prob all wrong lol!

$(document).ready(function() {
  // the stacks match the number of the div - div0 has stack 0 - this can be ajaxed or just present in the code. It is only text
  var stacks = [];

  $(".slideshow").mouseenter(function() {
  
		//Get data needed to source the right images to feed the ajax request
		//Not sure about this, HTML 5 compliant, don't know if this will work in older browsers etc...need to test
		var dataString=this.getAttribute("data-string"); //or jquery equivalent
		
		//Set idx to that of the div
		var idx = this.id.replace("div","");
		
		//Call the php script for images
		$.ajax({
			type: "POST",
			url: "getimages.php",
			data: dataString,
			cache: true,
			success: function(imgdata)
			{
				//js equivalent of 
				//imgarr = implode(',', imgdata); <- unless better more direct method
				//Add the imgarr to the stacks array in position of idx
				$("#anotherdivid").html(html);
			}
		});
	
    
    jQuery.preloadImages(this.id,stacks[idx])
  });    

});
</script>  
</head>
<body>
<div id="div0" class="slideshow" data-string="var1=blah&var2=blah&var3=blah">
  <img src="http://www.amnh.org/exhibitions/frogs/images/home/frogs_image.jpg" />
 
</div>  
</body>
</html>

Open in new window



0
 
dolythgoeAuthor Commented:
Ah soz, ignore the $("#anotherdivid").html(html); bit
0
 
dolythgoeAuthor Commented:
And explode not implode! php file will echo urls seperated by commas
0
 
Michel PlungjanIT ExpertCommented:
try this instead

Result from php should be a comma delimited string of urls as in
img1.jpg,image2.gif,picture3.png

Open in new window

<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
<script type="text/javascript">
var caches = {};
function addImages(container) {
  var images = caches[container].imgArr;
  var cont = $("#"+container);
  cont.empty();
  for (var i=0, n=images.length;i<n;i++) {
    cont.append('<img src="'+images[i].src+'"/>')
  }
  // here you can start the slideshow for this container 
}
(function($) {

  // Argument 0 is the container, Argument1 is an array of image paths relative to the current page.
  $.preloadImages = function() {
    var container = arguments[0];
    var cache = caches[container] = {};   
    caches[container].imgArr  = [];
    caches[container].stack = arguments[1];
    caches[container].counter = 0;
    caches[container].errors  = 0;
    caches[container].loaded  = 0;
    for (var i = imageArr.length; i--;) {
      var cacheImage = $('<img/>');
      cacheImage.bind("load",function() { cache.imgArr.push(this); cache.loaded++; cache.counter++;if (cache.counter>=cache.imgArr.length) addImages(container) })
      cacheImage.bind("error",function() { cache.errors++; cache.counter++; if (cache.counter>=cache.imgArr.length) addImages(container)  })
      cacheImage.attr("src",imageArr[i]);
    }
    $(".slideshow").unbind("mouseenter"); // remove the mouseOver we defined in the onload
  }
})(jQuery);

$(document).ready(function() {
  $(".slideshow").mouseenter(function() {
		var dataString=$(this).attr("data-string"); 
		var thisId = this.id; 
		//Set idx to that of the div
		var idx = thisId.replace("div","");
		//Call the php script for images
		$.ajax({
			type: "POST",
			url: "getimages.php",
			data: dataString,
			cache: true,
			success: function(imgdata) {
        // add the stack to the caches[divId], preloads and loads the div with the result			
			  jQuery.preloadImages(thisId,imgdata.split(",")); 
			}
		});
  });    
});
</script>  
</head>
<body>
<div id="div0" class="slideshow" data-string="var1=blah&var2=blah&var3=blah">
  <img src="http://www.amnh.org/exhibitions/frogs/images/home/frogs_image.jpg" />
 
</div>  
</body>
</html>

Open in new window

0
 
dolythgoeAuthor Commented:
Thanks - I recevied this error:

imageArr is not defined
[Break On This Error] for (var i = imageArr.length; i--;) {

I tried caches[container].imgArr.length but it didn't like that either!
0
 
Michel PlungjanIT ExpertCommented:
please try

success: function(imgdata) {
 alert(imgdata)
     // add the stack to the caches[divId], preloads and loads the div with the result                  
    jQuery.preloadImages(thisId,imgdata.split(","));
  }
0
 
dolythgoeAuthor Commented:
That's working ok, spits out the list of images delimited by ,

So the link up with the <div> data and the ajax request seem good..

I noticed it's imageArr when elsewhere it's imgArr but changing it didn't seem to work either :/

0
 
dolythgoeAuthor Commented:
I know what that feels like!

Unfortunately, firebug gave me this:

imageArr is not defined
[Break On This Error] cacheImage.attr("src",imageArr[i]); 

Open in new window

Is that another one of those rogue imageArr's? :)
0
 
Michel PlungjanIT ExpertCommented:
Yes

change
imageArr[i]

Open in new window

to
cache.stack[i]

Open in new window

0
 
dolythgoeAuthor Commented:
Awesome thanks again for all your help, much appreicated as I know it's not exactly easy stuff!!

Initial testing works when I mouseover an image when page refreshes but it will only act once - the functionality doesn't seem to repeat for other mouseovers. Could that be something to do with mouseenter tracking only once to class slideshow despite the several instances of it in the various divs?
0
 
Michel PlungjanIT ExpertCommented:
Not sure what you mean. I coded it so a Mouseover loads the stack for that div ONCE and the removes the stack loading mouse over since I could not imagine you wanted to load the same stack more than once.

0
 
dolythgoeAuthor Commented:
Yeah that's fine - I mean for all the different stacks. It loads one stack fine - any one of them on page reolad but if I move the mouse around over a few different ones it won't load any of those - only the first <div> to have the mouseover.

So I've got:

<div class="slideshow id=div0">
</div>
<div class="slideshow id=div1">
</div>
<div class="slideshow id=div2">
</div>
<div class="slideshow id=div3">
</div>
<div class="slideshow id=div4">
</div>
<div class="slideshow id=div5">
</div>
...more..

Open in new window


If I load the page and hover over...say...div4, it will load that stack but if I then move to any of the others, they don't respond.

Of course, like you said, it's only right to load one stack once.
0
 
Michel PlungjanIT ExpertCommented:
Aha then I misunderstood.
I know the problem - I unbind by class instead of by id
0
 
Michel PlungjanIT ExpertCommented:
Change
$(".slideshow").unbind("mouseenter");
To
$("#div"+idx).unbind("mouseenter");

I'll check it in about an hour

0
 
dolythgoeAuthor Commented:
Cool - that errors with:

idx is not defined
[Break On This Error] $("#div"+idx).unbind("mouseenter")

I guess need a way to pass that between functions?
0
 
Michel PlungjanIT ExpertCommented:
No I need to get to a real computer. Gimme 45

0
 
Michel PlungjanIT ExpertCommented:
$("#"+container).unbind("mouseenter"); // remove the mouseOver we defined in the onload

0
 
dolythgoeAuthor Commented:
That's done it :)

Well, thanks so much again for going a few extra miles on this one.

I'll close this off and move onto the next phase of rotating through them on continued mouseover. I have some script written down and I''ll have a bash before troubling experts once more!

Cheers
0
 
Michel PlungjanIT ExpertCommented:
Great :)

for next time, it is ok to accept one comment as solution if the ones you are accepting are all from the same expert - saves you a lot of clicking :)
0
 
dolythgoeAuthor Commented:
I'd thought I'd highlight the corrections too for the good of the community :)!
0
 
Michel PlungjanIT ExpertCommented:
Ah I thought you might, actually I already copied them to the main comment, because I can
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.