147

Is there a way to determine if a image path leads to an actual image, Ie, detect when an image fails to load in JavaScript.

For a web app, I am parsing a xml file and dynamically creating HTML images from a list of image paths. Some image paths may no longer exist on the server so I want to fail gracefully by detecting which images fail to load and deleting that HTML img element.

Note jQuery solutions wont be able to be used(the boss doesn't want to use jQuery, yes I know dont get me started). I know of a way in jQuery to detect when an image is loaded, but not whether it failed.

My code to create img elements but how can I detect if the img path leads to a failed to load image?

var imgObj = new Image();  // document.createElement("img");
imgObj.src = src;
Yves M.
  • 29,855
  • 23
  • 108
  • 144
sazr
  • 24,984
  • 66
  • 194
  • 362
  • 1
    This might help you: http://stackoverflow.com/questions/1977871/check-if-an-image-is-loaded-no-errors-in-javascript (it's jQuery, but it still may lead you on the right path) – Ben Lee Mar 22 '12 at 03:02
  • try one of these https://www.google.com/search?rlz=1C1CHJL_esMX444MX444&sourceid=chrome&ie=UTF-8&q=Detect+when+an+image+fails+to+load+in+Javascript#hl=en&rlz=1C1CHJL_esMX444MX444&sclient=psy-ab&q=site%3Ahttp%3A%2F%2Fstackoverflow.com+Detect+when+an+image+fails+to+load+in+Javascript&oq=site:http%3A%2F%2Fstackoverflow.com+Detect+when+an+image+fails+to+load+in+Javascript&aq=f&aqi=&aql=&gs_l=serp.12...4485l10141l0l11322l11l9l0l0l0l0l165l703l8j1l9l0.frgbld.&pbx=1&bav=on.2,or.r_gc.r_pw.r_qf.,cf.osb&fp=19d0ebe5924d5495&biw=1024&bih=653 – ajax333221 Mar 22 '12 at 03:08
  • 7
    Funny @ajax333221 this very question is first in the results of your link :) – SSH This Nov 07 '13 at 22:35
  • write a JQuery selector to find a new Boss... the internet is a big place. – JJS Apr 09 '19 at 20:24
  • 5
    http://needsmorejquery.com/ – fabb May 20 '19 at 13:23

9 Answers9

156

You could try the following code. I can't vouch for browser compatibility though, so you'll have to test that.

function testImage(URL) {
    var tester=new Image();
    tester.onload=imageFound;
    tester.onerror=imageNotFound;
    tester.src=URL;
}

function imageFound() {
    alert('That image is found and loaded');
}

function imageNotFound() {
    alert('That image was not found.');
}

testImage("http://foo.com/bar.jpg");

And my sympathies for the jQuery-resistant boss!

Signal
  • 917
  • 1
  • 9
  • 19
Nikhil
  • 3,042
  • 2
  • 16
  • 16
  • 2
    Any idea if there's a way to tell if it encounters a redirect? – quickshiftin Nov 18 '14 at 06:12
  • 4
    This doesn't work on webKit(Epiphany, Safari, ...) depending on the server you are getting an image from. For example, sometimes imgur don't send an image that exists nor an 404 status and the error event is not triggered in this case. –  Jun 04 '15 at 13:02
  • 1
    The problem with this is your message, "That image was not found." The response code could be a 500 or 403 or something else. You don't know that it was a 404. In fact it's not very likely that it's a 404 since you probably wouldn't try and load an image that isn't there. – PHP Guru Feb 12 '20 at 19:29
  • 1
    should add event handler instead of direct asignment – cdalxndr Mar 05 '20 at 18:28
  • doesn't work if the image contains errors – france1 Jan 27 '23 at 13:21
71

The answer is nice, but it introduces one problem. Whenever you assign onload or onerror directly, it may replace the callback that was assigned earlier. That is why there's a nice method that "registers the specified listener on the EventTarget it's called on" as they say on MDN. You can register as many listeners as you want on the same event.

Let me rewrite the answer a little bit.

function testImage(url) {
    var tester = new Image();
    tester.addEventListener('load', imageFound);
    tester.addEventListener('error', imageNotFound);
    tester.src = url;
}

function imageFound() {
    alert('That image is found and loaded');
}

function imageNotFound() {
    alert('That image was not found.');
}

testImage("http://foo.com/bar.jpg");

Because the external resource loading process is asynchronous, it would be even nicer to use modern JavaScript with promises, such as the following.

function testImage(url) {

    // Define the promise
    const imgPromise = new Promise(function imgPromise(resolve, reject) {

        // Create the image
        const imgElement = new Image();

        // When image is loaded, resolve the promise
        imgElement.addEventListener('load', function imgOnLoad() {
            resolve(this);
        });

        // When there's an error during load, reject the promise
        imgElement.addEventListener('error', function imgOnError() {
            reject();
        })

        // Assign URL
        imgElement.src = url;

    });

    return imgPromise;
}

testImage("http://foo.com/bar.jpg").then(

    function fulfilled(img) {
        console.log('That image is found and loaded', img);
    },

    function rejected() {
        console.log('That image was not found');
    }

);
emil.c
  • 1,987
  • 2
  • 26
  • 46
  • 2
    maybe i'm missing something, but how can the assignment (tester.onload=..) replace a callback, if the "tester" was just created? even if some obscure part of the code adds an event to every image created, it's not obvious that we want to keep those events for the purpose of detecting if an image exists. – jvilhena Apr 15 '17 at 18:24
  • 3
    You are absolutely correct. In this particular case it may not be a problem because it's highly likely that it's not gonna be replaced. However, in general, adding an event listener is better practice than assigning a method directly. – emil.c Apr 16 '17 at 20:07
  • Modern JavaScript without arrow functions and async/await? :) – John Weisz Jun 19 '17 at 09:22
  • 1
    @JohnWeisz Arrow functions are anonymous, therefore it's harder to debug, use them carefully. – emil.c Jun 19 '17 at 12:38
  • @emil.c when you are calling new Promise() its standard to use an arrow function as you are using an anonymous func anyway. They are meant to be used primarily where you already have an anonymous func so your statement is ridiculous. – GifCo Sep 26 '17 at 18:02
  • @GifCo have you read [Kyle Simpsons books](https://github.com/getify/You-Dont-Know-JS)? Arrow functions should be primarily used to bind `this` context to a function, it's purpose is not the short notation. I wouldn't call it a standard. And thanks for noticing, I'm adding a name to the anonymous function. And what I was pointing out with my statement were the `fulfilled` and `rejected` functions in `.then()` because these are usually replaced by arrow functions, which leads to confusing errors. – emil.c Sep 27 '17 at 09:36
  • 12
    @GifCo I don't feel comfortable continuing such discussion with you. I feel offended by your language. If you believe my answer is incorrect or incomplete, suggest changes and explain your reasoning. If you think something is BAD practice, suggest a good practice with your own answer. – emil.c Sep 28 '17 at 19:08
  • 3
    Why is there a discussion about arrow functions here? It is off topic. The answer is fine just how it is especially since old browsers don't support arrow functions (Internet Explorer for instance). – PHP Guru Feb 13 '20 at 07:47
  • 1
    I would rather not use `addEventListener` when it's unnecessary, nor lead beginners to think it's a silver bullet. Messing with event bubbling or cancelling propagation inside a handler can be enough to break something. People should not tinker with events if they don't know what they're doing. In that case, "good practices" are rather a problem than a solution, in my opinion. – kuroi neko Jul 31 '21 at 00:32
55

This:

<img onerror="this.src='/images/image.png'" src="...">
Rafael Martins
  • 561
  • 4
  • 3
10
/**
 * Tests image load.
 * @param {String} url
 * @returns {Promise}
 */
function testImageUrl(url) {
  return new Promise(function(resolve, reject) {
    var image = new Image();
    image.addEventListener('load', resolve);
    image.addEventListener('error', reject);
    image.src = url;
  });
}

return testImageUrl(imageUrl).then(function imageLoaded(e) {
  return imageUrl;
})
.catch(function imageFailed(e) {
  return defaultImageUrl;
});
holmberd
  • 2,393
  • 26
  • 30
5

jQuery + CSS for img

With jQuery this is working for me :

$('img').error(function() {
    $(this).attr('src', '/no-img.png').addClass('no-img');
});

And I can use this picture everywhere on my website regardless of the size of it with the following CSS3 property :

img.no-img {
    object-fit: cover;
    object-position: 50% 50%;
}

TIP 1 : use a square image of at least 800 x 800 pixels.

TIP 2 : for use with portrait of people, use object-position: 20% 50%;

CSS only for background-img

For missing background images, I also added the following on each background-image declaration :

background-image: url('path-to-image.png'), url('no-img.png');

NOTE : not working for transparent images.

Apache server side

Another solution is to detect missing image with Apache before to send to browser and remplace it by the default no-img.png content.

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} /images/.*\.(gif|jpg|jpeg|png)$
RewriteRule .* /images/no-img.png [L,R=307]
Meloman
  • 3,558
  • 3
  • 41
  • 51
4

More to the answer of Rafael Martins, if you are using React, use this code:

<img
  src="https://example.com/does_not_exist.png"
  onError={(e) => {
    e.currentTarget.src = "https://example.com/default.png"
  }}
/>
Jerome Leclanche
  • 587
  • 3
  • 11
WestMountain
  • 136
  • 1
  • 1
  • 8
3

This is a variation of @emil.c answer but resolves with true/false based on image loading or not (as opposed to throwing an error when it fails):

function testImage(url) {
  if (!url || typeof url !== 'string') return Promise.resolve(false);
  return new Promise((resolve) => {
    const imgElement = new Image();
    imgElement.addEventListener('load', () => resolve(true));
    imgElement.addEventListener('error', () => resolve(false));
    imgElement.src = url;
  });
}
timmcliu
  • 1,769
  • 1
  • 13
  • 12
3

Here's a function I wrote for another answer: Javascript Image Url Verify. I don't know if it's exactly what you need, but it uses the various techniques that you would use which include handlers for onload, onerror, onabort and a general timeout.

Because image loading is asynchronous, you call this function with your image and then it calls your callback sometime later with the result.

Community
  • 1
  • 1
jfriend00
  • 683,504
  • 96
  • 985
  • 979
-20

just like below:

var img = new Image(); 
img.src = imgUrl; 

if (!img.complete) {

//has picture
}
else //not{ 

}
JamesChen
  • 14
  • 3
  • 4
    Nope. This might clue you in to whether or not the image is cached, in some browsers -- but without a load callback, you're not waiting for even a moment to give the browser a chance to initiate an HTTP request for that image. – ChaseMoskal Oct 28 '13 at 19:56
  • this is just for saying something! – Hitmands Mar 16 '16 at 08:29
  • 5
    Image loading is asynchronous. – emil.c Sep 19 '16 at 13:26
  • @emil.c it's not always, for some reason. ChaseMoskal (whom SO won't let me notify...) has it right - this works only if the image finishes loading before JS execution continues, which seems to happen whenever it's cached. – twhb Sep 30 '16 at 18:18