I am loading a large number of images into a dynamic DIV and I am using a preloader to get the images.
imageObj = new Image();
imageObj.src = imgpath + imgname;
Each of these events creates a GET that I can see and monitor in Firebug.
If I know the name and path of an image, can I watch the relevant XMLHttpRequest to see if the GET has completed?
I do not want to rely on (or use) .onload events for this process.
The pseudo would look something like this...
if (imageObj.GET = 'complete')
Has anyone had any experience of this?
EDIT 1
Thanks to the help from Bart (see below) I have changed my image preloader to store an array of the image objects...
function imagePreLoader(imgname) {
images[imgnum] = new Image();
images[imgnum].src = imgpath + imgname;// load the image
imgnum ++;
}
And then, after all my other functions have run to build the content DIVs, I used the image.complete attribute in the following...
var interval = setInterval(function () {
imgcount = imgnum - 1; // because the imgnum counter ++ after src is called.
ok = 1;
for (i=0; i<imgcount; i++) {
if (images[i].complete == false){
ok = 0;
}
}
if (ok == 1) {
clearInterval(interval);
showIndexOnLoad();
}
}, 1000);
This waits until all the images are complete and only triggers the showIndexOnLoad() function when I get the 'ok' from the interval function.
All images now appear as I wanted, all at once with no additional waits for the GETs to catch up.
Well done Bart for putting me on to the image.complete attribute.
You can watch the complete property of the image to see if the image is fully loaded or not.
Here's an example.
http://jsfiddle.net/t3esV/1/
function load (source) {
var img = new Image();
img.src = source;
console.log('Loading ' + source);
var interval = setInterval(function () {
if (img.complete) {
clearInterval(interval);
complete(img);
}
}, 400);
};
function complete(img) {
console.log('Loaded', img.src);
document.body.appendChild(img);
};
Note: This example fails to clear the interval when something goes wrong and complete is never set to true.
Update
I wrote a simple jQuery.preload plugin to take advantage of the image.complete property.
This is a very interesting problem, and I am afraid there is no actual solution to this. The load event for images is when the image is being rendered and the browser knows the width and height of it.
What you would be after would be a tag-applicable readystatechange event. Alas, only IE allows you to bind those to non-document elements, so this is not an option.
There are a bunch of plug-ins that allow you to go around it, as well. One pretty hot one is https://github.com/desandro/imagesloaded , which has the added advantage of dealing with all the browser differences very efficiently. It, however, still relies on the load event (and I am pretty sure this is the only way to start doing what you want to do).
Related
To give you some background, many (if not all) websites load their images one by one, so if there are a lot of images, and/or you have a slow computer, most of the images wont show up. This is avoidable for the most part, however if you're running a script to exact image URLs, then you don't need to see the image, you just want its URL. My question is as follows:
Is it possible to trick a webpage into thinking an image is done loading so that it will start loading the next one?
Typically browser will not wait for one image to be downloaded before requesting the next image. It will request all images simultaneously, as soon as it gets the srcs of those images.
Are you sure that the images are indeed waiting for previous image to download or are they waiting for a specific time interval?
In case if you are sure that it depends on download of previous image, then what you can do is, route all your requests through some proxy server / firewall and configure it to return an empty file with HTTP status 200 whenever an image is requested from that site.
That way the browser (or actually the website code) will assume that it has downloaded the image successfully.
how do I do that? – Jack Kasbrack
That's actually a very open ended / opinion based question. It will also depend on your OS, browser, system permissions etc. Assuming you are using Windows and have sufficient permissions, you can try using Fiddler. It has an AutoResponder functionality that you can use.
(I've no affiliation with Fiddler / Telerik as such. I'm suggesting it only as an example and because I've used it in the past and know that it can be used for the aforementioned purpose. There will be many more products that provide similar functionality and you should use the product of your choice.)
use a plugin called lazy load. what it does is it will load the whole webpage and will just load the image later on. it will only load the image when the user scroll on it.
To extract all image URLs to a text file maybe you could use something like this,
If you execute this script inside any website it will list the URLs of the images
document.querySelectorAll('*[src]').forEach((item) => {
const isImage = item.src.match(/(http(s?):)([/|.|\w|\s|-])*\.(?:jpg|jpeg|gif|png|svg)/g);
if (isImage) console.log(item.src);
});
You could also use the same idea to read Style from elements and get images from background url or something, like that:
document.querySelectorAll('*').forEach((item) => {
const computedItem = getComputedStyle(item);
Object.keys(computedItem).forEach((attr) => {
const style = computedItem[attr];
const image = style.match(/(http(s?):)([/|.|\w|\s|-])*\.(?:jpg|jpeg|gif|png|svg)/g);
if (image) console.log(image[0]);
});
});
So, at the end of the day you could do some function like that, which will return an array of all images on the site
function getImageURLS() {
let images = [];
document.querySelectorAll('*').forEach((item) => {
const computedItem = getComputedStyle(item);
Object.keys(computedItem).forEach((attr) => {
const style = computedItem[attr];
const image = style.match(/(http(s?):)([/|.|\w|\s|-])*\.(?:jpg|jpeg|gif|png|svg)/g);
if (image) images.push(image[0]);
});
});
document.querySelectorAll('*[src]').forEach((item) => {
const isImage = item.src.match(/(http(s?):)([/|.|\w|\s|-])*\.(?:jpg|jpeg|gif|png|svg)/g);
if (isImage) images.push(item.src);
});
return images;
}
It can probably be optimized but, well you get the idea..
If you just want to extract images once. You can use some tools like
1) Chrome Extension
2) Software
3) Online website
If you want to run it multiple times. Probably use the above code https://stackoverflow.com/a/53245330/4674358 wrapped in if condition
if(document.readyState === "complete") {
extractURL();
}
else {
//Add onload or DOMContentLoaded event listeners here: for example,
window.addEventListener("onload", function () {
extractURL();
}, false);
//or
/*document.addEventListener("DOMContentLoaded", function () {
extractURL();
}, false);*/
}
extractURL() {
//code mentioned above
}
You want the "DOMContentLoaded" event docs. It fires as soon as the document is fully parsed, but before everything has been loaded.
let addIfImage = (list, image) => image.src.match(/(http(s?):)([/|.|\w|\s|-])*\.(?:jpg|jpeg|gif|png|svg)/g) ?
[image.src, ...list] :
list;
let getSrcFromTags= (tag = 'img') => Array.from(document.getElementsByTagName(tag))
.reduce(addIfImage, []);
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", doSomething);
} else { // `DOMContentLoaded` already fired
doSomething();
}
I am using this, works as expected:
var imageLoading = function(n) {
var image = document.images[n];
var downloadingImage = new Image();
downloadingImage.onload = function(){
image.src = this.src;
console.log('Image ' + n + ' loaded');
if (document.images[++n]) {
imageLoading(n);
}
};
downloadingImage.src = image.getAttribute("data-src");
}
document.addEventListener("DOMContentLoaded", function(event) {
setTimeout(function() {
imageLoading(0);
}, 0);
});
And change every src attribute of image element to data-src
I'm trying to load image with JS and give it a certain time to load. For example 1 second. If the image wasn't loaded within 1 second - use default image. Here's what I have so far:
function displayUserImage() {
setTimeout(function(){
var imageMe = new Image();
imageMe.onload = function(){
$('#user-img-me').css('background-image', 'url(' + imageMe.src + ')');
};
imageMe.onerror = function() {
imageMe.src = "images/defaultImage.png";
}
if(!isEmpty(myObject.getPic()) && !isBlank(myObject.getPic())) {
imageMe.src = myObject.getPic();
}
else {
imageMe.src = "images/defaultImage.png";
}
}, 0);
}
I'm setting timeOut of 0 only to make the image loading asynchronous and not to interfere with execution of rest of the code. myObject.getPic() returns a string with image path. The image may be located on a remote domain/server. The connection might be slow, the image might be heavy, the server may not respond... So I want to give this image only a second to load, and if it didn't succeed - use default image. In both cases I need to set external variable imageMeLoaded to true once either image is loaded. Can it be done?
Sorry but, I've had a look at similar posts on Stack Overflow and could not find my exact situation.
I'm iterating on a bunch of images and I want to exit as soon as I find the one meeting my specifications. Trouble is, I need to use an 'onload' event to test that.
I don't know how to break out of my innermost function: the each() loop always iterates on all items, even though the second image fits the bill. Here's the jsfiddle: you will see 3 alerts, one for each iteration. http://jsfiddle.net/q07awnbr/10/
If anyone could guide me, that would be awesome! Thx.
// A bunch of images
var arrImages = ["http://i.imgur.com/cKUVXuQ.jpg","http://i.imgur.com/Ei598tR.jpg","http://i.imgur.com/W92PhqU.jpg"];
// Iterate until I find the first one meeting my specs
$.each(arrImages, function(i,item)
{
extension = item.slice(-(3)).toLowerCase();
if (extension == "jpg")
{
// Test image size
newImg = new Image();
newImg.onload = function()
{
if (this.width > 600 && this.height > 900)
{
// All right! That's the one. Set it as src on my web page
$("#MyImgBg").attr("src",this.src);
return false; // trying to break out - not working
}
};
newImg.src = item;
}
// I expected this alert to popup only twice
alert(i);
});
The following loads one image at a time and checks if it is the correct size, if not it loads the next one. It stops once the correct image has been loaded.
// A bunch of images
var arrImages = ["http://i.imgur.com/cKUVXuQ.jpg","http://i.imgur.com/Ei598tR.jpg","http://i.imgur.com/W92PhqU.jpg"];
// Loads image with index i
var loadImage = function(i){
extension = arrImages[i].slice(-(3)).toLowerCase();
if (extension == "jpg"){
// Test image size
var newImg = new Image();
newImg.src = arrImages[i];
newImg.onload = function(){
if (this.width > 600 && this.height > 900){
// All right! That's the one. Set it as src on my web page
$("#MyImgBg").attr("src",this.src);
}else{
if(i < arrImages.length){
// This is not the one, load next one.
loadImage(i+1);
}
}
}
}else{
if(i < arrImages.length){
// Wrong file extension, try next one.
loadImage(i+1);
}
}
alert(i);
}
loadImage(0); // Start with first image
The onload handler is asynchronous so it runs AFTER the .each() loop has finished. Thus, you can't stop the .each() from inside the onload handler.
If you want to load the images one at a time and only load the next one if the previous one doesn't meet your criteria, then you will need a totally different structure to the code. You will not be able to use $.each() the way you are. Instead, you will have to start loading of the next image from within the onload handler of the previous one (thus serializing the asynchronous image loads).
I have a div in which I am appending imgs. Images are loading with AJAX by 20 items. They are showing with jQuery animation. A problem that animation is retarding. I think that there are 2 problems:
Images for showing are not downloaded and i want to display them now
When I set css display: block to img, I get delay when browser painting image
So the question is how to wait for pack of images (20 items) is downloaded and how to dispose of the painting delay?
P/S. If I show empty divs (just set them background-color) or 1 image for all img-tags the animation works quickly.
You can use this snippet. It doesn't depend on any library and is working well for our projects.
/*
* Preload an image if not cached, then call a callback function
* #param {String} the image url
* #param {Function} the callback function
*/
function loadImg(url, fn) {
var img = new Image();
img.src = url;
if (img.complete) { // If the image has been cached, just call the callback
if (fn) fn.call(img, true);
} else {
img.onerror = function() { // If fail to load the image
if (fn) fn.call(img, false);
};
img.onload = function() { // If loaded successfully
if (fn) fn.call(img, true);
//On IE6, multiple frames in a image will fire the 'onload' event for multiple times. So set to null
img.onload = null;
};
};
}
For your scenario, you can pass in a callback function like:
var callback = function(loaded) {
if (loaded === true) {
// do the animation.
var img = this; // now `this` points to the image itself.
} else {
// show default image or 'image failed to load'.
}
}
You can use my jQuery plugin to wait for the images to download in your new element.
$('#container').waitForImages().done(function() {
$(this).addClass('ready');
});
Use .complete of javascript
<img src="http://www.zastavki.com/pictures/originals/2013/Photoshop_Image_of_the_horse_053857_.jpg" id="something" />
var myVar = setInterval(function(){
console.log("loading")
if( document.getElementById("something").complete ) { // checks if image is loaded
console.log( "loaded" );
clearInterval(myVar);
}
}, 500);
here is jsfiddle demo.
You can either preload the images ahead of time (so they are all ready in the cache and won't have any delay when loading) or you can load them when needed and use a notification when they are loaded before starting the animation. Either way, you need to make sure the images are loaded before starting the animation if you want a smooth running animation.
You can see these other references for how to preload images with code samples. The first reference supports a callback that will get called when all the images have been loaded:
Image preloader javascript that supports events
Is there a way to load images to user's cache asynchronously?
How do you cache an image in Javascript
Searching for a js script, which will show some message (something like "Loading, please wait") until the page loads all images.
Important - it mustn't use any js framework (jquery, mootools, etc), must be an ordinary js script.
Message must disappear when the page is loaded.
Yeah an old-school question!
This goes back to those days when we used to preload images...
Anyway, here's some code. The magic is the "complete" property on the document.images collection (Image objects).
// setup a timer, adjust the 200 to some other milliseconds if desired
var _timer = setInterval("imgloaded()",200);
function imgloaded() {
// assume they're all loaded
var loaded = true;
// test all images for "complete" property
for(var i = 0, len = document.images.length; i < len; i++) {
if(!document.images[i].complete) { loaded = false; break; }
}
// if loaded is still true, change the HTML
if(loaded) {
document.getElementById("msg").innerHTML = "Done.";
// clear the timer
clearInterval(_timer);
}
};
Of course, this assumes you have some DIV thrown in somewhere:
<div id="msg">Loading...</div>
Just add a static <div> to the page, informing user that the page is loading. Then add window.onload handler and remove the div.
BTW, what’s the reason of this? Don’t users already have page load indicators in their browsers?
You should do async ajax requests for the images and add a call back when it's finished.
Here's some code to illustrate it:
var R = new XMLHttpRequest();
R.onreadystatechange = function() {
if (R.readyState == 4) {
// Do something with R.responseXML/Text ...
stopWaiting();
}
};
Theoretically you could have an onload event on every image object that runs a function that checks if all images is loaded. This way you don´t need a setTimeOut(). This would however fail if an image didn´t load so you would have to take onerror into account also.