image.onload not firing twice in IE7 - javascript

It works in IE6, and FireFox; but for some reason not in IE7.
Using ASP.NET on the Page_Init I populate a list of chapters that are links to the image in the book as well as a Javascript array which holds the pageIDs.
ex.
Chapter 1 --> href="javascript:seePage(4);"
Here is the actual code I am using:
var availablePages = ['1002_001','1002_002','1002_003','1002_004','1002_005'];
function seePage(index) {
$get('imgSingle').src = 'graphics/loading.gif';
var img = new Image();
img.src = 'get.jpg.aspx?size=single&id=' + availablePages[index];
img.onload = function() {
var single = $get('imgSingle');
single.src = img.src;
}
}
When I click on Chapter 1, the image loads fine across the board (IE6,7,FF) and clicking on a second chapter link also works; however, in (and only in) IE7 does clicking on the same chapter twice (chap1, chap2, then chap1 again) does the image get stuck on the 'loading' image...

It's caused because IE will cache the image, and the onload event will never fire after it's already been loaded.
You need to position the onload event before the src.
var availablePages = ['1002_001','1002_002','1002_003','1002_004','1002_005'];
function seePage(index) {
$get('imgSingle').src = 'graphics/loading.gif';
var img = new Image();
img.onload = function() {
var single = $get('imgSingle');
single.src = img.src;
}
img.src = 'get.jpg.aspx?size=single&id=' + availablePages[index];
}

Another way of doing this is to check if the image is already loaded with image.complete. For instance:
var img = new Image();
img.src = 'foo.jpg';
if(img.complete){
img.onload = function(){ /* ... */ };
} else {
/* execute something else, or the same. */
}
I've experienced this behavior in IE6 and 7.

Related

Is an event triggered when an HTML link element (<a/>) containing base64 data as href is ready?

I have created a webpage that basically displays 2 images side by side.
It has a "download" button, which triggers a vanilla Javascript function, which creates a <canvas> HTML element and concatenates the two images inside of it. It then creates a link with the base64-encoded result image as href and clicks on it:
<a download="image.png" id="dllink" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABQAAAAMnCAYAAABhnf9DAAAgAElEQVR4nOzdR48kD3rn96j03pfv6qo21dVd3qT3JryP9Jll281..."></a>
Here is what the function I'm using looks like:
/**
* Create canvas, draw both images in it, create a link with the result
* image in base64 in the "href" field, append the link to the document,
* and click on it
*/
function saveImage() {
// Get left image
var imgLeft = new Image();
imgLeft.setAttribute('crossOrigin', 'anonymous');
imgLeft.src = "imgleft/" + idxImageShownLeft + ".jpg";
imgLeft.onload = function() {
// Once the left image is ready, get right image
var imgRight = new Image()
imgRight.setAttribute('crossOrigin', 'anonymous');
imgRight.src = "imgright/" + idxImageShownRight + ".jpg";
imgRight.onload = function() {
// Once the right image is ready, create the canvas
var canv = document.createElement("canvas");
var widthLeft = parseInt(imgLeft.width);
var widthRight = parseInt(imgRight.width);
var width = widthLeft + widthRight;
var height = imgLeft.height;
canv.setAttribute("width", width);
canv.setAttribute("height", height);
canv.setAttribute("id", "myCanvas");
canv.setAttribute('crossOrigin', 'anonymous');
var ctx = canv.getContext("2d");
// Draw both images in canvas
ctx.drawImage(imgLeft, 0, 0);
ctx.drawImage(imgRight, widthLeft, 0);
// Create PNG image out of the canvas
var img = canv.toDataURL("image/png");
// Create link element
var aHref = document.createElement('a');
aHref.href = img;
aHref.setAttribute("id", "dllink");
aHref.download = "image.png";
// Append link to document
var renderDiv = document.getElementById("render");
renderDiv.replaceChild(aHref, document.getElementById("dllink"));
// Click on link
aHref.click();
}
}
}
My problem is that this works fine on Firefox, but not on Chrome.
After a bit of investigating, I realized that by setting a breakpoint before the aHref.click(); line in Chrome, it worked fine. I think that it means that the aHref.click(); is called before the <a href="data:image/png;base64,...></a> is ready to be clicked, but I don't know for sure.
I couldn't find a duplicate of this topic. What keywords should I use just to be 100% sure?
Am I investigating in the right direction?
Is there an event I could rely on in order to call aHref.click(); only when it is ready?
You could wrap it in an init function that gets called when the window completes loading.
function init() {
aHref.click();
}
window.onload = init;
Its similar to the vanilla equivalent of jQuery's .ready() method.
aHref , document.getElementById("dllink") appear to be same element ? Though "dllink" has not yet been appended to document when .replaceChild called ?
Try substituting
renderDiv.appendChild(aHref);
for
renderDiv.replaceChild(aHref, document.getElementById("dllink"));

Make a new image appear by clicking on the previous image

I want to write a code in which when you click on an image another image appears. After that when you click on the new image, another one appears, and so on.
I wrote this code which works for the first image. I can't figure out how to define the appeared images as inputs.
var i = 1
function addimage() {
var img = document.createElement("img");
img.src = "images/d" + i + ".jpg";
document.body.appendChild(img);
}
function counter() {
i = i + 1
}
<input type="image" src="images/d1.jpg" onclick="addimage(); counter();">
Attach an onclick function to the new image, with the same code as in your input tag:
var i = 1
function imageClick() {
if (! this.alreadyClicked)
{
addimage();
counter();
this.alreadyClicked = true;
}
}
function addimage() {
var img = document.createElement("img");
img.src = "http://placehold.it/" + (200 + i);
img.onclick = imageClick;
document.body.appendChild(img);
}
function counter() {
i = i + 1
}
<input type="image" src="http://placehold.it/200" onclick="imageClick();">
To add an event handler to an element, there are three methods; only use one of them:
=> With an HTML attribute. I wouldn't recommend this method because it mixes JS with HTML and isn't practical in the long run.
<img id="firstImage" src="something.png" onclick="myListener(event);" />
=> With the element's attribute in JS. This only works if you have a single event to bind to that element, so I avoid using it.
var firstImage = document.getElementById('firstImage');
firstImage.onclick = myListener;
=> By binding it with JavaScript. This method has been standardized and works in all browsers since IE9, so there's no reason not to use it anymore.
var firstImage = document.getElementById('firstImage');
firstImage.addEventListener("click", myListener);
Off course, myListener needs to be a function, and it will receive the event as its first argument.
In your case, you probably don't want to add another image when you click on any image that isn't currently the last. So when a user clicks on the last image, you want to add a new image and stop listening for clicks on the current one.
var i = 1;
function addNextImage(e) {
// remove the listener from the current image
e.target.removeEventListener("click", addNextImage);
// create a new image and bind the listener to it
var img = document.createElement("img");
img.src = "http://placehold.it/" + (200 + i);
img.addEventListener("click", addNextImage);
document.body.appendChild(img);
// increment the counter variable
i = i + 1;
}
var firstImage = document.getElementById("firstImage");
firstImage.addEventListener("click", addNextImage);
Try on JSFiddle
On a side note: while JavaScript does support omitting some semi-columns it's considered a better practice to put them, and it will avoid small mistakes.

Something goes wrong with fadeIn

Hello I want to fadeOut image, and then do fadeIn with a new one, so I wrote a simple code, but something goes wrong, because when .photo img fadesOut, then fadesIn this same photo, but after, a few second its changes because of new "src", but even if browser didn't load a new image, the old one shound't show, becuase src is changed, but it shows, and after a second, maybe two changes to the new one. Can somebody tell me what's wrong?
var dimage = $next.children("img").attr("rel");
$(".photo img").fadeOut("slow", function () {
$(".photo img").attr("src", dimage);
$(".photo img").fadeIn("slow");
});
This may be because the image has to load after the src is altered.
Consider putting the image in a tag, then setting the css property to display:none. This way the image will preload in the browser before your script runs and will be available when it does.
you aren't giving the new image enough time to load.
function loadImage (src) {
return $.Deferred(function(def){
var img = new Image();
img.onload = function(){
def.resolve(src);
}
img.src = src;
}).promise();
}
var dimage = $next.children("img").attr("rel");
var imageLoadedDef = loadImage(dimage);
$(".photo img").fadeOut("slow", function () {
def.done(function(src){
$(".photo img").attr("src", src);
$(".photo img").fadeIn("slow");
});
});
the problem as highlighted is about images not ready for display when you call them, so the solution is to preload them before starting the slideshow, create a function with an array of images path
function preLoad(){
var imgs = {'test1.jpg', 'test2.jpg', 'test3.jpg'};
var img = document.createElement('img');
for(var i = 0; i < imgs.leght; i++){
img.src = imgs[i]; //all images gets preloaded at this stage
}
startSlider(); //here you will do your code
}

Why does this "loading message" script not work in FF?(javascript)

I have this script which should show the text "Loading..." while images are loading, then change the text to "loaded" when all images are loaded. I added a button to load new images to make sure that it works for dynamically loaded images as well.
This works perfectly in Chrome but in Firefox the "Loading..." text never appears. I have no idea why this would be. The page begins loading and not all images are loaded so it should create the text "Loading.." but it doesn't. Then when all images are done loading the text "Loading" appears.
I just don't get why one message would appear and the other wouldn't. Especially because there are no qualifications that have to be met before creating the "Loading..." text, it should just fire automatically.
jsfiddle Example | Full Page Example
$(document).ready(function() {
var checkComplete = function() {
if($('img').filter(function() {return $('img').prop('complete');}).length == $('img').length) {
$('.status').text('Loaded');
} else {
$('.status').text('Loading...');
}
};
$('img').on('load',function() {
checkComplete();
});
$('#button').click(function() {
$('img.a').attr('src' , 'http://farm9.staticflickr.com/8545/8675107979_ee12611e6e_o.jpg');
$('img.b').attr( 'src' , 'http://farm9.staticflickr.com/8382/8677371836_651f586c99_o.jpg');
checkComplete();
});
checkComplete();
});
You have several issues in the code.
First off, the checkComplete() function is not written correctly. It should be this:
var checkComplete = function() {
var imgs = $('img');
if(imgs.filter(function() {return this.complete;}).length == imgs.length) {
$('.status').text('Loaded');
} else {
$('.status').text('Loading...');
}
};
The main fix here is that the filter callback needs to refer to this.complete, not to $('img').prop('complete') because you are trying to filter a single item at a time.
Second off, you are relying on both .complete and .load working correctly AFTER you've changed the .src value. This is explicitly one of the cases where they do not work properly in all browsers.
The bulletproof way to work around this is to create a new image object for the new images, set the onload handler before you set the .src value and when both onload handlers have fired, you will know that both new images are loaded and you can replace the once you have in the DOM with the new ones.
Here is a version that works in FF:
$(document).ready(function() {
$('#button').click(function() {
var imgA = new Image();
var imgB = new Image();
imgA.className = "a";
imgB.className = "b";
var loaded = 0;
imgA.onload = imgB.onload = function() {
++loaded;
if (loaded == 2) {
$("img.a").replaceWith(imgA);
$("img.b").replaceWith(imgB);
$('.status').text('Loaded');
}
}
// the part with adding now to the end of the URL here is just for testing purposes to break the cache
// remove that part for deployment
var now = new Date().getTime();
imgA.src = 'http://farm9.staticflickr.com/8545/8675107979_ee12611e6e_o.jpg?' + now;
imgB.src = 'http://farm9.staticflickr.com/8382/8677371836_651f586c99_o.jpg?' + now;
$('.status').text('Loading...');
});
});
Working demo: http://jsfiddle.net/jfriend00/yy7GX/
If you want to preserve the original objects, you can use the newly created objects only for preloading the new images and then change .src after they've been preloaded like this:
$(document).ready(function() {
$('#button').click(function() {
var imgA = new Image();
var imgB = new Image();
var loaded = 0;
imgA.onload = imgB.onload = function() {
++loaded;
if (loaded == 2) {
$("img.a")[0].src = imgA.src;
$("img.b")[0].src = imgB.src;
$('.status').text('Loaded');
}
}
// the part with adding now to the end of the URL here is just for testing purposes to break the cache
// remove that part for deployment
var now = new Date().getTime();
imgA.src = 'http://farm9.staticflickr.com/8545/8675107979_ee12611e6e_o.jpg?' + now;
imgB.src = 'http://farm9.staticflickr.com/8382/8677371836_651f586c99_o.jpg?' + now;
$('.status').text('Loading...');
});
});
Working demo of this version: http://jsfiddle.net/jfriend00/ChSQ5/
From the jQuery API .load method
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

Capturing load event for images dynamically changing their source

I am changing src for image dynamically and want to capture on load event for that image. Is there a way I can do it?
Several years ago, I discovered that the onload event was not reliable in some browsers (I don't remember which ones) when setting .src for the second time. As such, I settled for replacing the image object. I would create a new image object, set the onload handler, set the .src value and then when it loads, insert it into the page in place of the existing image.
When creating an image from scratch in javascript, you do this:
var img = new Image();
img.onload = function() {
// put your onload code here
};
img.src = "xxx.jpg"
If you just want to change the .src of an image, you just find the DOM object in the page and set it's .src property.
var img = document.getElementById("myImage");
img.src = "xxx.jpg";
If you want to try to capture the onload event when resetting the .src (what I had reliability problems with a couple years ago, you would do this:
var img = document.getElementById("myImage");
img.onload = function() {
// your code here
};
img.src = "xxx.jpg";
If you want to load a new image and replace an existing one with it when it loads, you would do this:
function replaceImg(oldImage, newSrc) {
var img = new Image();
img.onload = function() {
var parent = oldImage.parentNode;
parent.insertBefore(img, oldImage);
parent.removeChild(oldImage);
// put any other code you want here when the
// replacement image is loaded and in place
};
img.src = newSrc;
}
And, you would call this like this:
<img id="myImage" src="yyy.jpg">
var oldImage = document.getElementById("myImage");
replaceImg(oldImage, "xxx.jpg");
// create image object
var image = new Image();
image.onload = function () {
// do stuff
$('#myimageelementid').prop('src', image.src); // set the element in the DOM with this image
}
image.src = "image.jpg"; // will trigger onload when loaded

Categories