Loading an image but onload/onerror not working as expected - javascript

I have a div
<div id='cards'>
Which I want to fill with images based on some logic. But only when images are first loaded into memory. Otherwise, through onerror I wanna fill in some text..
function pasteCard(card, to){
if (typeof(card) == 'string')
card = [card];
var image = [];
for (var i = 0; i < card.length; i++) {
image[i] = new Image();
image[i].src = '/sprites/open/' + card[i] + '.png';
image[i].onload = function() {
pasteImage(to, image[i]);
}
image[i].onerror = function() {
pasteText(to, card[i]);
}
// alert(card[i]) #1
}
function pasteImage(to, image) {
to.append(image);
}
function pasteText(to, text) {
// alert(card[i]) #2
to.append(text);
}
}
pasteCard(['ABC123', 'DEF456', 'GHI789'], $('#cards'));
But this isn't working.
Problem/weirdness: If only #2 alert is active it returns nothing. But strangely if #1 alert is also active it does kinda work... (but still doesn't load my images, and mostly fails too when other code is involved)
Question: Why is it not working without #1 alert (at least in that jsfiddle)
suggestions?: what should I do?

Onload and onerror events are fired (executed) outside the scope of your function so your variables will be undefined. In the event method you have access to this which is the image object. You can set a data attribute to each image and access that in your error event.
Here is an example:
http://jsfiddle.net/7CfEu/4/

The callbacks are not in the same scope as your image array is - therefor you need to declare a variable then will "connect the scopes" and use it inside the callbacks
also the i variable probably changes until the callback is fired - so by using it inside the callback you will get undefined behavior
for (var i = 0; i < card.length; i++) {
var current_card = card[i];
var current_image = new Image();
current_image.onload = function() {
pasteImage(to, current_image);
}
current_image.onerror = function() {
pasteText(to, current_card);
}
current_image.src = '/sprites/open/' + current_card + '.png';
image[i] = current_image;
}
Fiddle: http://jsfiddle.net/7CfEu/6/
(Also - closing the div tag is never a bad idea)

Just in case anyone ends up here for same reason I did.
Was going crazy because onload and onerror were not firing in the page I was building. Tried copy pasting
var myimage = new Image();
myimage.onload = function() { alert("Success"); };
myimage.onerror = function() { alert("Fail"); };
myimage.src = "mog.gif" //Doesn't exist.
Which was working within codepen and random otherwise blank pages.
Turns out the problem I was having was that I was doing AJAX requests earlier in the page. This involved authorization which in turn involved a call to
setRequestHeader();
This was resulting in a net::ERR_FILE_NOT_FOUND error instead of the expected GET mog.gif 404 (Not Found)
This seemed to prevent proper triggering of events.
Revert with
xhr.setRequestHeader("Authorization", "");

Related

Dynamically change image in javascript

JavaScript Noob here.
I want to show the dynamically changing image effect like this:
http://dotsignals.org/google_popup.php?cid=202
(if this webpage cannot show changing image, click on any camera at http://dotsignals.org)
I have examined the code, and have some questions:
1.The website uses setImage and refreshSetImage to initialize and refresh the image for a dynamically changing effect. What is the use of Math.random in here? I understand it is a way to differentiate images from different time at the same cam. But how does it works? How would the backend respond?
2.Related to the first question. What is the mechanism of the refreshSetImage? I didn't see any sign of requesting data from the server. Does it send a "GET"? How does it refresh the image?
function setImage(imageID){
var currentImage = imageID;
document.getElementById(currentImage).src ='http://207.251.86.238/cctv9.jpg'+'?math=';
refreshSetImage();
}
function refreshSetImage() {
document.images["myCam"].src = 'http://207.251.86.238/cctv9.jpg'+'?math='+Math.random();
setTimeout('refreshSetImage()',1000);
}
function ScaleSize(imageID) {
var elem = document.getElementById(imageID);
elem.style.width = 500;
elem.style.height = 300;
}
3.How is the backend designed?
4.Now I want to use these two functions in my own project. I want to add a parameter of camID in the setImage and refreshSetImage functions, so the src of the image will change to something like: 'http://..../'+camID+'.jpg'+'?math'. camID is a String that identifies different cameras. I changed it to:
function refreshSetImage(camID) {
document.images["myCam"].src = 'http://207.251.86.238/'+camID+'.jpg'+'?math='+Math.random();
setTimeout('refreshSetImage(camID)',1000);
}
function setImage(imageID,camID){
var currentImage = imageID;
document.getElementById(currentImage).src = 'http://207.251.86.238/'+camID+'.jpg'+'?math=';
refreshSetImage(camID);
}
and got an error :Uncaught ReferenceError: camID is not defined VM132:1. The image is not changing as well. I don't know what is the problem. What is VM132:1 ?
Thanks
check
function refreshSetImage(imageID,camID) {
currentImage = imageID;
url = 'http://207.251.86.238/'+camID+'.jpg'+'?math='+Math.random();
//document.getElementById(currentImage).src= url;
console.log(url);
}
$(document).ready(function(){
imageID = "imageID";
camID = "camID";
setInterval(function() {
refreshSetImage("imageID","camID");
}, 3000);
});
https://jsfiddle.net/Cuchu/vr1ftwg1/

Load content in div from a href tag in jQuery

I want to load all images before displaying them in a slideshow. I have been searching a lot but nothing seems to work. This is what i have so far and it doesn't work. I am loading images from the <a> tag from another div.
$('.slideshow').load(function(){
if (loaded<length){
first = $(settings.thumb).eq(loaded).find('a').attr("href");
$('<img src="'+first1+'"/>').appendTo('.slideshow');
}
else{ $('.slideshow').show(); }
loaded++;
});
Add an event listener to each image to respond to when the browser has finished loading the image, then append it to your slideshow.
var $images = $("#div_containing_images img");
var numImages = $images.length;
var numLoaded = 0;
var $slideshow = $(".slideshow");
$images.each(function() {
var $thisImg = $(this);
$thisImg.on("load", function() {
$thisImg.detach().appendTo($slideshow);
numLoaded++;
if (numLoaded == numImages) {
$slideshow.show();
}
});
});
It's a good idea to also listen for the error event as well, in case the image fails to load. That way you can increase numLoaded to account for broken image. Otherwise, your slideshow will never be shown in the event the image is broken.
Also note, that by calling detach() followed by appendTo() I am am moving the image in the DOM. If instead, you want to copy the image, use clone() instead of detach().
* EDIT TO MODIFY USER'S EXACT USE CASE *
var $images = $("li.one_photo a");
var numImages = $images.length;
var numLoaded = 0;
$images.each(function() {
$('<img />',
{ src: $(this).attr("href") })
.appendTo('.slideshow')
.on("load error", function() {
numLoaded++;
if(numLoaded == numImages) {
$('.slideshow').show();
}
});
});
* EDIT #2 *
Just realized you were putting everything in the $(".slideshow").load() function. Since $(".slideshow") represents a DIV, it will never raise a load event, and the corresponding function will never execute. Edited above accordingly.

Can I "pre-load" an image with JS to be used as a CSS background-image?

Can images be preemptively loaded into the page with javascript so that they can be used at any time as a CSS background image without any request/upload delay?
If so, how?
You don't even need to use JS for this (with the downside of delaying the page load event). Include something like this:
<img src="/path/to/image.jpg.png.gif.bmp" style="display: none" />
This will trigger a request for the image, and add it to the local cache. When you set the CSS background-image property, the image will already be in the local cache, eliminating the delay of another request.
Alternatively, you can accomplish the same thing without delaying the page load by creating the images in JavaScript (this solution allows for multiple images):
function preload(list, callback, imageCallback) {
var at, len;
at = len = list.length;
for (var i = 0; i < len; i++ ) {
var img = new Image();
img.onload = function() {
if( imageCallback ) {
imageCallback.call(this, this, len-at, len);
}
if( !--at ) {
callback(list);
}
};
img.src = list[i];
list[i] = img;
}
}
You'd call this with:
var list = preload(["1.png","2.png","3.png" ... ], function complete(list) {
console.log('images all loaded!');
}, function loaded(image, index, listCount) {
console.log('image ' + index + ' of + 'listCount + 'is loaded');
});
(Thanks to #rlemon for the preload code)
I don't think that using an hidden img tag is the correct way, i'd rather use an "new Img(url)" and attaching to it an onload event where you can set the image as background-image to the element you want.
img = new Image();
img.onload = function(){
// set background-image
};
img.src = image_url;
be sure to put img.src after attaching onload, or you risk that the image is loaded before the event is attached.
Maybe a more complete base to build on:
function preload(list, callback, imageCallback, errorCallback) {
if (typeof(list) === "undefined"
|| list.length === 0) {
return;
}
var len = list.length;
var timers = {};
var checkLen0 = function() {
if (len === 0) {
if (typeof(callback) === "function") {
callback();
}
delete(timers)
}
}
var onload = function() {
clearTimeout(timers[img]);
if (typeof(imageCallback) === "function") {
imageCallback.call(img);
}
len--;
checkLen0();
}
var onerror = function() {
clearTimeout(timers[img]);
if (typeof(errorCallback) === "function") {
errorCallback.call(img);
}
len--;
checkLen0();
}
for (var i = 0; i < list.length; i++ ) {
var img = new Image();
img.onload = onload;
timers[img] = window.setTimeout(5000, onerror);
img.src = list[i];
}
}
While SomeKittens answer is valid, it'll delay the page load as commented by Jimmy. If you are using jquery, I'd go with something like this instead to keep your style, structure and logic separated:
<style>
.preload-img { display: none; }
</style>
...
<div class = "preload-img">/path/to/image.jpg.png.gif.bmp</div>
...
<script>
$(document).ready(function(){
$(".preload-img").each(function(){
preloadImage = new Image();
preloadImage.src = $(this).html();
});
});
</script>
Of course, from there on you can optimize/change it. The advantadge of this is that you can create the <div> dynamically with PHP and you can have all your javascript cached properly as a separated file.
There is an excellent framework for this job called Emerge.js
http://ilyabirman.net/projects/emerge/
Quote from project page:
Emerge.js is a framework for coordinated page loading. Normally, when a complex web page is loading, images appear in random order, causing unpleasant flashing. To replace it with nice and coordinated animations, programming is required. Emerge.js simplifies the task by removing the need to write any Javascript code. The framework uses a declarative approach, where you specify a desired behavior for each element and do not think about the implementation. Emerge.js uses jQuery.
Just as a caveat to SomeKittens' answer above, a particularly large background image should probably be added as a hidden background / via JavaScript after page load, as content images delay the firing of window.onload and may therefore create the perception of a slow loading page.
Given that it sounds like you're using dynamic content anyway, this may be an acceptable solution. It also allows you to do preloading programmatically as required, which may be better for maintenance.

Sending information through a for loop

At the moment I'm making another html5/javascript ad. This one uses multiple data from cookies. In order to render everything properly I have made a for-loop that gets the information and then sends it to another function afterwards.
My problem is that it loads too quick the first time so it skips the images. Each time I have to refresh the ad before it shows properly. All the onload functions I have used failed so far (sending the wrong data, nothing at all or only 1 piece of it).
Here is the relevant part of the project:
for (var i=0; i < 3; i++){
pT.push("pT"+[i]);
pM.push("pM"+[i]);
ctx.push("ctxP"+[i]);
cvs.push("cvsP"+[i]);
cvs[i] = document.getElementById('canvas'+[i]);
ctx[i] = cvs[i].getContext('2d');
pT[i] = new Image();
pT[i].onload = function(){
console.log("pT: "+ this.height);
}
pT[i].src = data.products[i].imageUrl;
pM[i] = new Image();
pM[i].onload = function(){
console.log("pM: "+ this.height);
}
pM[i].src = data.products[i].imageUrl;
testing(pT[i], pM[i]);
}
function testing(pThumb, pMain){
console.log(pThumb.height);
}
What I want is a method so all the information gets send when everything is done loading.
pT[i].onload = function() {
alert("Image is Loaded, do you thing on the image here");
}
otherFunction(pT,pM);
function otherFunction(pT,pM) {
console.log(pT,pM);
}
pT[i] = new Image();
pT[i].onload = function(){
pM[i] = new Image();
pM[i].onload = function(){
console.log("pM: "+ this.height);
testing(pT[i], pM[i]);
}
}
pT[i].src = data.products[i].imageUrl;
pM[i].src = data.products[i].imageUrl; // this is very ugly but you'll be sure to call your function when both image are ready. Since i'm not the best with Canvas, I will accept any edit for a better code.

Return address of image or error if it doesn't exist

I know my problem is the fact that I can't check if the image is good until it has time to load. I'm trying to check width and return after it or the src changes (using onerror to set an src.) I'm always getting stuck with a race condition though, and it errors out long before the height and width or src change. When I reload, the image is cached and it works fine. I don't know how to avoid this. Here is my current code (also not working, it loops until Firefox complains.) I'm setting the return of this function to a variable. There are some writes in there I'm using to see how far it gets, and it stops in the while loop. I tried using getTimeout('tstit = chkload("thisisatest",tinypth);',250);, but that didn't work either. I wish I could force this to load in order...
function buildimg(tehname,myc,pth1,pth2,tinypth)
{
var myimg = pth1+tehname+"/"+tehname+"-"+myc+".jpg";
buildtest("thisisatest",myimg,tinypth);
var testimg = document.getElementById("thisisatest");
var tstit = chkload("thisisatest",tinypth);
while(tstit == false) {
document.write(tstit);
tstit = chkload("thisisatest",tinypth);
}
alert(testimg.src+'-'+testimg.width+'-'+testimg.height);
if((testimage.width > 20) && (testimage.height > 20)) {
return myimg;
}
else if(typeof pth2 == "undefined") {
myimg = "error";
return myimg;
}
else {
myimg = buildimg(tehname,myc,pth2);
return myimg;
}
document.write('No return error in buildimg.');
return "error";
}
/*Builds a hidden img tag for testing images*/
function buildtest(itsid,itssrc,tinypath) {
if(document.getElementById(itsid)) {
var imgobj = document.getElementById(itsid);
imgobj.remove();
}
document.write('<img id="'+itsid+'" style="display: none;" src="'+itssrc+'" onerror="swaponerr(\''+itsid+'\',\''+tinypath+'\')" />');
}
/*Swaps the image to a small picture, so we can detect if it worked*/
function swaponerr(tagid, tinypath) {
var theimg = document.getElementById(tagid);
theimg.onerror = '';
theimg.src = tinypath;
}
/*Recurses to return when the image is loaded*/
function chkload(loadid,tinychk) {
var tehobj = document.getElementById(loadid);
document.write(tehobj.width+'x'+tehobj.height+'x'+tehobj.src);
if((tehobj.naturalWidth > 20) && (tehobj.naturalHeight > 20)) {
return true;
}
if(tehobj.src == tinychk) {
return true;
}
return false;
}
I need to test for an image, and return error if it is non-existent. The code below works fine on my server:
/*Checks if the image at /tehname/tehname-c.jpg exists in either of the paths
outputs it's address if it does, and "error" if not.*/
function buildimg(tehname,index,pth1,pth2)
{
var myimg = pth1+tehname+"/"+tehname+"-"+index+".jpg";
$.ajax({
url:myimg,
type:'HEAD',
error: function()
{
myimg=pth2+tehname+"/"+tehname+"-"+index+".jpg";
$.ajax({
url:myimg,
type:'HEAD',
error: function()
{
myimg="error";
return;
},
});
return;
},
});
return myimg;
}
Unfortunately, I'm trying to do this on a messed up system my work uses. We do have jquery, but the system stores user files on a separate server from code, so ajax won't work. This will eventually be in a .js file, I hope.
Now I've got code starting with:
function buildimg(tehname,myc,pth1,pth2)
{
var myimg = pth1+tehname+"/"+tehname+"-"+myc+".jpg";
var tehimage = new Image();
tehimg.src = myimg;
I tried to have the function load the image, and check its width, but I always get 0, since I can't pre-load the images without knowing how many they are (and I don't want to have some outrageously high number of requests with most being errors.) For some reason (at least on Firefox 4, as that's what my goal is to get working first) tehimage.complete always returns false. I've tried using onerror and onload by a global variable, and a few other methods. I must admit though, I'm not very versed in Javascript, so my callbacks may not have worked.
Please help, I'm getting desperate!
Do you hold the 'power to execute php code' on that user files server? If so, check them in php and output to images.js in that server, than in your html, first load images.js, then your usual javascript.
By the way, jQuery had memory leaks in ajax for ears, don't know about 1.6, but 1.5 definately had those. if you wan't to use ajax to get data from server to javascript - use plain javascript: http://www.w3schools.com/ajax/ajax_xmlhttprequest_send.asp
Edit: Definately worth checking out: http://fettig.net/weblog/2005/11/28/how-to-make-xmlhttprequest-connections-to-another-server-in-your-domain/
Since I'm getting no answers, I'm closing the question. What I ended up doing was a modification to make the image change class in the onload, have the image onerror delete it, and have the rest of the code run in the window onload. Unfortunately, this means the 404s aren't caught before it tries to load more images, so I'm forced to limit the max number of images that can be used (changeable in the function call,) and the images that aren't there just waste a little time.
See the final result here.

Categories

Resources