sorry if this thread is repited but i didn't find it in browser.
I want to create/handle an event that let me know when a specific resource load into the web page. (similar to onload or DOMContentLoaded, BUT i need to know when each resource finish, not the whole page)
Example--> Assets = image1,image2,image3,...
when finish loading image1 trigger a ready event,
After ready event triggered start loading image2
keep going...keep going....
After last image, trigger again ready event.
The final result is the same as DOMContentLoaded or "windows.onload", but as i said im not abble to put some logic in the middle of assets load.
Someone know how to handle that kind of thing?
I am not sure, why (or if) you want to 'load' the images in a specific order. Be aware that browsers rarely open a single connection. Normally all resources will be collected (after downloading the html itself) and the browser will load many of them in parallel. In short - in case you do this for performance or speed, you would slow down the process!
A more common approach to lazy load images is using the viewport / scroll position to decide which images should be loaded "next", there are a few jquery plugins, e.g. lazyload.
Anyway - in case you do not care about the order and you just want to a element specific callback when ready you could do something like this:
$("img").one("load", function() {
var $this = this;
// 'this' is your specific element
// do whatever you like to do onready
}).each(function() {
// handle cached elements
if(this.complete) $(this).load();
});
In case you do care about the order and you really want to load the next image after the first one is ready you need a different approach.
First: Your HTML does not contain image sources, but images with a data-attribute(s):
<img data-src="the/path/to/your/image" data-assets-order="1" />
Second: In JS you collect all these images without a real source, you order the collection and finally trigger the loading one after each other.
var toLoad = [];
// scanning for all images with a data-src attribute
// and collect them in a specified order.
$('img[data-src]').each(function() {
// either you define a custom order by a data-attribute (data-assets-order)
// or you use the DOM position as the index. Mixing both might be risky.
var index = $(this).attr('data-assets-order') ?
$(this).attr('data-assets-order') : toLoad.length;
// already existing? put the element to the end
if (toLoad[index]) { index = toLoad.length; }
toLoad[index] = this;
});
// this method handles the loading itself and triggers
// the next element of the collection to be loaded.
function loadAsset(index) {
if (toLoad[index]) {
var asset = $(toLoad[index]);
// bind onload to the element
asset.on("load", function() {
// in case it is ready, call the next asset
if (index < toLoad.length) {
loadAsset(index + 1);
}
});
// set the source attribut to trigger the load event
asset.attr('src', asset.attr('data-src'));
}
}
// we have assets? start loading process
if (toLoad.length) { loadAsset(index); }
I'm not sure if this will work for you, but did you tried the setTimeOut function?
You can set a specific time for your image to load (through ajax, you can make the call to the resource and wait until request.done), and after that, call to the next one.
I also found something 'similar' to your request here:
How to wait for another JS to load to proceed operation?
Sorry if this doesn't help you.
Related
I want to implement a plug-in serial download pictures in MooTools. Let's say there are pictures with the img tag inside a div with the class imageswrapper. Need to consistently download each image after it loads the next and so on until all the images are not loaded.
window.addEvent('domready', function(){
// get all images in div with class 'imageswrapper'
var imagesArray = $$('.imageswrapper img');
var tempProperty = '';
// hide them and set them to the attribute 'data-src' to cancel the background download
for (var i=0; i<imagesArray.length; i++) {
tempProperty = imagesArray[i].getProperty('src');
imagesArray[i].removeProperty('src');
imagesArray[i].setProperty('data-src', tempProperty);
}
tempProperty = '';
var iterator = 0;
// select the block in which we will inject Pictures
var injDiv = $$('div.imageswrapper');
// recursive function that executes itself after a new image is loaded
function imgBomber() {
// exit conditions of the recursion
if (iterator > (imagesArray.length-1)) {
return false;
}
tempProperty = imagesArray[iterator].getProperty('data-src');
imagesArray[iterator].removeProperty('data-src');
imagesArray[iterator].setProperty('src', tempProperty);
imagesArray[iterator].addEvent('load', function() {
imagesArray[iterator].inject(injDiv);
iterator++;
imgBomber();
});
} ;
imgBomber();
});
There are several issues I can see here. You have not actually said what the issue is so... this is more of a code review / ideas for you until you post the actual problems with it (or a jsfiddle with it)
you run this code in domready where the browser may have already initiated the download of the images based upon the src property. you will be better off sending data-src from server directly before you even start
Probably biggest problem is: var injDiv = $$('div.imageswrapper'); will return a COLLECTION - so [<div.imageswrapper></div>, ..] - which cannot take an inject since the target can be multiple dom nodes. use var injDiv = document.getElement('div.imageswrapper'); instead.
there are issues with the load events and the .addEvent('load') for cross-browser. they need to be cleaned up after execution as in IE < 9, it will fire load every time an animated gif loops, for example. also, you don't have onerror and onabort handlers, which means your loader will stop at a 404 or any other unexpected response.
you should not use data-src to store the data, it's slow. MooTools has Element storage - use el.store('src', oldSource) and el.retrieve('src') and el.eliminate('src'). much faster.
you expose the iterator to the upper scope.
use mootools api - use .set() and .get() and not .getProperty() and .setProperty()
for (var i) iterators are unsafe to use for async operations. control flow of the app will continue to run and different operations may reference the wrong iterator index. looking at your code, this shouldn't be the case but you should use the mootools .each(fn(item, index), scope) from Elements / Array method.
Anyway, your problem has already been solved on several layers.
Eg, I wrote pre-loader - a framework agnostic image loader plugin that can download an array of images either in parallel or pipelined (like you are trying to) with onProgress etc events - see http://jsfiddle.net/dimitar/mFQm6/ - see the screenshots at the bottom of the readme.md:
MooTools solves this also (without the wait on previous image) via Asset.js - http://mootools.net/docs/more/Utilities/Assets#Asset:Asset-image and Asset.images for multiple. see the source for inspiration - https://github.com/mootools/mootools-more/blob/master/Source/Utilities/Assets.js
Here's an example doing this via my pre-loader class: http://jsfiddle.net/dimitar/JhpsH/
(function(){
var imagesToLoad = [],
imgDiv = document.getElement('div.injecthere');
$$('.imageswrapper img').each(function(el){
imagesToLoad.push(el.get('src'));
el.erase('src');
});
new preLoader(imagesToLoad, {
pipeline: true, // sequential loading like yours
onProgress: function(img, imageEl, index){
imgDiv.adopt(imageEl);
}
});
}());
I'm working on a website which uses ExpressionEngine to create a list of images with img1, img2, img3 etc as the ID and creates an array with their sources imgAddresses[1], imgAddresses[2], imgAddresses[3] etc.
I'm attempting to create a function which loads the first image, then (when the first is completely loaded), load the second, third etc. The following is what I have so far:
function loadImage(counter) {
var i = document.getElementById("img"+counter);
if(counter==imgAddresses.length) { return; }
i.onload = function(){
loadImage(counter+1)
};
i.src = imgAddresses[counter];
}
document.onload=loadImage(0);
It works when refreshing the page, but not when accessing the page via the URL. As far as I can tell from research, this is because the onload event is not fired when a cached image is loaded, and refreshing the page clears the cache, whereas accessing the page via the URL does not.
Research suggests that assigning the src of the image after declaring the onload event would get around this, but it does not seem to have solved it in this case. I was thinking that this may be because the onload event is recursive in this case.
Does anyone have any ideas on how to make sure the browser is loaded a fresh copy of the image, rather than a cached version? Or whether there is a better way to write this function? Thanks for any help!
EDIT: One solution that I have found is to change the img source assignment to:
i.src = imgAddresses[counter] + '?' + new Date().getTime();
This forces the user to load a fresh copy each time, which I guess is not so much a solution, but a workaround
The only thing I can say is that you are not attaching the document.onload handler correctly. I cannot tell if it will fix your issue because image.onload is not reliable in all browsers, however the onload should be set to a function reference and that's not what you are doing.
Instead, you should have something like:
function loadImage(counter) {
//initialize the counter to 0 if no counter was passed
counter = counter || 0;
var i = document.getElementById("img"+counter);
if(counter==imgAddresses.length) { return; }
i.onload = function(){
loadImage(counter+1)
};
i.src = imgAddresses[counter];
}
document.onload = loadImage; //instead of loadImage(0);
You can tell how the browser will manage the cached resources
Take a look to HTML5 cache approach:
HTML5: The cache manifest file
This way you can avoid the browser cache for the specified resources.
Is there any way to know how far browser loaded the page?
Either by using JavaScript or browser native functions.
Based on the page status i want to build progress bar.
I'm not 100% sure this will work, but.. here is the theory:
First of all, don't stop JavaScript running until the page has loaded. Meaning, don't use window.ready document.ready etc..
At the top of the page initialise a JavaScript variable called loaded or something and set it to 0.
var loaded = 0;
Throughout the page increment loaded at different points that you consider to be at the correct percentages.
For example, after you think half the page would have been loaded in the code set loaded = 50;.
Etc..
As I say, this is just a concept.
Code:
function request() {
showLoading(); //calls a function which displays the loading message
myRequest = GetXmlHttpObject();
url = 'path/to/script.php';
myRequest.open('GET',url,true);
myRequest.onreadystatechange = function() {
if(myRequest.readyState == 4 && myRequest.status == 200) {
clearLoading(); //calls a function which removes the loading message
//show the response by, e.g. populating a div
//with the response received from the server
}
}
myRequest.send(null);
}
At the beginning of the request I call showLoading() which uses Javascript to dynamically add the equivalent of your preLoaderDiv. Then, when the response is received, I call clearLoading() which dynamically removes the equivalent of your preLoaderDiv.
You'll have to determine it yourself, and to do that you'll have to have a way to determine it. One possibility is to have dummy elements along the page, know their total, and at each point count how many are already present. But that will only give you the amount of DOM obtained, and that can be a very small part of the load time - most often than not, the browser is idle waiting for scripts and images.
Lets say I have a div, and want to inject into that div some HTML. The HTML will include 1 or more images.
Is there any way with plain JavaScript or jQuery to determine when the images have loaded?
So if I do the following, can you place an event listener onto a single element to tell when the contents, including images, is ready?:
var html = "<p>Some text <img src='image.jpg' /></p><p>Some more text <img src='image2.jpg' /></p>";
$("div.output").html(html);
The images will be different every time so I cannot preload using JavaScript image object.
One method I'm thinking of doing is running through the HTML with regular expressions or jQuery, finding all image URLs, loop through them and preload each with the image object. When all have been preloaded, then inject the HTML into the output div.
Unless you hook up an onload event to each image before it loads and count them up, there's no easy way to tell when they're all loaded.
Here's a relatively bulletproof method (demo) that uses Deferreds, tested and working in IE 6-9, Chrome 19, and Firefox 3.6-10, Opera 10.10-11.52, Android 4, and iOS 5.
First, we'll write a small jQuery plugin that returns an array of Deferreds for each element in the jQuery collection. Each Deferred will be resolved when the element loads; or rejected if the element fails to load or (optionally) takes longer than timeout seconds.
$.fn.loaded = function(opts) {
var o = $.extend({timeout:10000}, opts) // Merge default options with supplied options
, r = []; // Return value
this.each(function() {
var dfd = new $.Deferred(), el = $(this), to;
if (o.timeout) to = setTimeout(function() {
done();
dfd.reject();
}, o.timeout);
el.bind('load.dfdl', function() {
done();
dfd.resolve();
}).bind('error.dfdl', function() {
done();
dfd.reject();
});
function done() { // internal clean-up
clearTimeout(to);
el.unbind('.dfdl');
}
r.push(dfd.promise());
});
return r;
};
The timeout will guard against cases where the browser never actually fires any events. I've set the default timeout to 10 seconds here; in the real world you might want to reduce that.
Now we'll generate 10 randomly-sized placekittens to use as images in the example.
var imgs=[];
for (var i = 0; i < 10; i++) imgs.push('<img src="http://placekitten.com/' + rnd() + '/' + rnd() + '"> ');
$('#imgs').html(imgs.join());
Finally, we'll put everything together with some magic:
$.when.apply($, $('#imgs img').loaded({timeout:10000}) ).done(function() {
alert('loaded successfully');
}).fail(function() {
alert('load failed or timed out');
});
$.when creates a master Deferred that gets resolved only when all of its child Deferreds resolve, or rejects when a child rejects. It normally expects you to pass each Deferred as an argument (it does not support passing in an array of Deferreds), so we have to apply our array. (I might do $.whenall = function(dfds) { $.when.apply($,dfds); }; so that your app code has a cleaner $.whenall( $('#imgs img').loaded() )...)
Try this:
$("#preload_div").html(
'<img src="image.png" alt="preload" class="preload" width="0" height="0">'
);
The event handler can be bound to the image:
$('preload').load(function () {
var html = "<p>Some text <img src='image.jpg' /></p><p>Some more text <img src='image2.jpg' /></p>";
$("div.output").html(html);
});
Of course, you would have to incorporate an .each() for each image to loop through all images.
EDIT: On one of my AJAX websites, I preload the next image I need, so when a user clicks the 'next' button, the image that the new content needs is already cached. See http://funl.es/p/1
One method I'm thinking of doing is running through the HTML with regular expressions or jQuery, finding all image URLs, loop through them and preload each with the image object. When all have been preloaded, then inject the HTML into the output div.
var imagesLoaded = [];
var imagesErrored = [];
$('img').each(function() {
$(this).load(function() {
imagesLoaded.push($(this).attr("src"));
if( (imagesLoaded.length + imagesErrored.length) == $('img').length ) {
displayResults(imagesLoaded, imagesErrored);
}
});
$(this).error(function() {
imagesErrored.push($(this).attr("src"));
if( (imagesLoaded.length + imagesErrored.length) == $('img').length ) {
displayResults(imagesLoaded, imagesErrored);
}
});
function displayResults(imagesLoaded, imagesErrored) {
for(var i = 0; i < imagesLoaded.length; i++) {
alert(imagesLoaded[i] + " has loaded successfully.");
$('#myDiv').append("<img src='" + imagesLoaded[i] + "' />");
}
for(var i = 0; i < imagesErrored.length; i++) {
alert(imagesLoaded[i] + " did NOT load successfully.");
}
}
The above loops through all images and checks whether or not they loaded, using the load event. Using the error event, we also get a collection of all images that for whatever reason did not load correctly. After all images have been preloaded, we can then inject them onto the page in the "displayResults" function. This is based loosely on the last paragraph in your question.
However, it doesn't use regular expressions, as they are not the right tool for manipulating the DOM. Fortunately, JavaScript and jQuery provide us with the tools we need to perform DOM manipulations and parsing activities.
Finally, from your question, it sounded like you may have been interested in knowing what images loaded successfully. The above collections that are built from the load and error events will provide you with those details, should you need them.
I think you should bind the load handler after you insert the images into the html.
DEMO
//Add image
$('#result').append('<img src= "<Your_Image_URL>" width="300" height="300"/>');
//Bind handler also add condition to not bind images that was not added before.
$('#result').find('img').not('.loaded').on('load', function () {
alert('Image Loaded');
//Add code that you want to do after the image is added
}).addClass('loaded');
Also please be aware of Caveats of the load event when used with images
A common challenge developers attempt to solve using the .load()
shortcut is to execute a function when an image (or collection of
images) have completely loaded. There are several known caveats with
this that should be noted. These are:
It doesn't work consistently nor reliably cross-browser
It doesn't fire correctly in WebKit if the image src is set to the same src as before
It doesn't correctly bubble up the DOM tree
Can cease to fire for images that already live in the browser's cache
Reference: http://api.jquery.com/load-event/
Have you considered using the jQuery 'on' event mechanism? Using 'on' should allow you to automatically bind 'load' handlers to each image as it gets dynamically added to the container and DOM.
Link: jQuery docs for'on' function
If you're using pre 1.7 jQuery, you'll need to use the 'live' function since 'on' was added with the 1.7 release.
Link: jQuery docs for 'live' function (deprecated)
I think that there is no easy way to do that. The thing that you could do is to load all the images on a div that is hidden ( display: none ).
When you want to show the pictures you can do:
$("#myDiv").show();
I don't want to know a way to preload images, I found much on the net, but I want to know how it works.
How is javascript able to preload images?
I mean, I tried a snippet from here, and even if it works, it doesn't seem to preload images.
When I check firebug, I can see that the image is loaded twice, once while the preloading, another time when displaying it!
To improve this code I'd like to know how it works.
Here is what i do:
function preload(arrayOfImages) {
$(arrayOfImages).each(function(){
$('<img/>')[0].src = this;
//(new Image()).src = this;
alert(this +' && ' + i++);
});
}
then i do something like that:
preloader = function() {
preload(myImages);
}
$(document).ready(preloader);
Here is how i display/add the image :
$("li.works").click(function() {
$("#viewer").children().empty();
$('#viewer').children().append('<img src=\'images/ref/'+this.firstChild.id+'.jpg\' alt="'+this.firstChild.id+'" \/>')
$("#viewer").children().fadeIn();
Your basic Javascript preloader does this:
var image = new Image();
image.src = '/path/to/the/image.jpg';
The way it works is simply by creating a new Image object and setting the src of it, the browser is going to go grab the image. We're not adding this particular image to the browser, but when the time comes to show the image in the page via whatever method we have setup, the browser will already have it in its cache and will not go fetch it again. I can't really tell you why whatever you have isn't working this way without looking at the code, though.
One interesting gotcha that is discussed in this question is what happens when you have an array of images and try preloading them all by using the same Image object:
var images = ['image1.jpg','image2.jpg'];
var image = new Image();
for(var x = 0; x < images.length; x++) {
image.src = images[x];
}
This will only preload the last image as the rest will not have time to preload before the loop comes around again to change the source of the object. View an example of this. You should be able to instantly see the second image once you click on the button, but the first one will have to load as it didn't get a chance to preload when you try to view it.
As such, the proper way to do many at once would be:
var images = ['image1.jpg','image2.jpg'];
for(var x = 0; x < images.length; x++) {
var image = new Image();
image.src = images[x];
}
Javascript preloading works by taking advantage of the caching mechanism used by browsers.
The basic idea is that once a resource is downloaded, it is stored for a period of time locally on the client machine so that the browser doesn't have to retrieve the resource again from across the net, the next time it is required for display/use by the browser.
Your code is probably working just fine and you're just misinterpeting what Fire Bug is displaying.
To test this theory just hit www.google.com with a clean cache. I.e. clear your download history first.
The first time through everything will likely have a status of 200 OK. Meaning your browser requested the resource and the server sent it. If you look at the bottom on the Fire Bug window it will says how big the page was say 195Kb and how much of that was pulled from cache. In this case 0Kb.
Then reload the same page without clearing your cache, and you will still see the same number of requests in FireBug.
The reason for this is simple enough. The page hasn't changed and still needs all the same resources it needed before.
What is different is that for the majority of these requests the server returned a 304 Not Modified Status, so the browser checked it's cache to see if it already had the resource stored locally, which in this case it did from the previous page load. So the browser just pulled the resource from the local cache.
If you look at the bottom of the Fire Bug window you will see that page size is still the same (195Kb) but that the majority of it, in my case 188Kb, was pulled locally from cache.
So the cache did work and the second time i hit Google I saved 188Kb of download.
I'm sure you will find the same thing with preloading your images. The request is still made but if the server returns a status of 304 then you will see that the image is in fact just pulled from local cache and not the net.
So with caching, the advantage is NOT that you kill off all future resource requests, i.e. a Uri lookup is still made to the net but rather that if possible the browser will pull from the local cache to satisify the need for the content, rather than run around the net looking for it.
You may be confused by the concept of "preloading". If you have a bunch of images in your HTML with <img src="...">, they cannot be preloaded with Javascript, they just load with the page.
Preloading images with Javascript is about loading images not already in the document source, then displaying them later. They are loaded after the page has rendered for the first time. They are preloaded in order to eliminate/minimize loading time when it comes to making them appear, for example when changing an image on mouse rollover.
For most applications, it is usually better practice to use "CSS sprites" as a form of preloading, in lieu of Javascript. SO should have a ton of questions on this.
It just involves making a new DOM image object and setting the src attribute. Nothing clever and AFAIK, it has always worked for me.
Is it possible the second "load" firebug is showing you is it loading it from cache?
The index on the loop is only looking
at the first image. Change it to use
the index:
function preload(arrayOfImages) {
$(arrayOfImages).each(function(i){ // Note the argument
$('<img/>')[i].src = this; // Note the i
//(new Image()).src = this;
alert(this +' && ' + i++);
});
}
Edit: In retrospect, this was wrong and I can see you're trying to create image elements. I don't understand why the index is there at all, there need not be an index. I think the function should look like this:
function preload(arrayOfImages) {
$(arrayOfImages).each(function () {
$('<img/>').attr('src', this);
});
}
And to instantiate it, why not just do this:
$(function () { // Equivalent to $(document).ready()
preload(myImages);
});
JavaScript image preloading works because when a DOM element that contains an image is created, the image is downloaded and cached. Even if another request is made when the image is actually rendered from the HTML, the server will send back a 304 (not changed), and the browser will simply load the image from its cache.
Paolo suggests using the following notation to create an image object:
var image = new Image();
While this will work, the DOM-compliant way of doing this is:
var image = document.createElement('img');
image.setAttribute('src', 'path/to/image.jpg');
Which is the way it is being done in the script, except it's using jQuery's HTML string literal syntax to do it. Additionally, most modern browsers offer compatibility with the Image() constructor by simply calling DOM-standard methods. For example, if you open up the Google Chrome JavaScript console and type Image, this is what you'll get:
function Image() {
return document.createElementNS('http://www.w3.org/1999/xhtml', 'img');
}
Chrome merely uses the native DOM methods to create an image element.