87

I'm writing some Javascript to resize the large image to fit into the user's browser window. (I don't control the size of the source images unfortunately.)

So something like this would be in the HTML:

<img id="photo"
     src="a_really_big_file.jpg"
     alt="this is some alt text"
     title="this is some title text" />

Is there a way for me to determine if the src image in an img tag has been downloaded?

I need this because I'm running into a problem if $(document).ready() is executed before the browser has loaded the image. $("#photo").width() and $("#photo").height() will return the size of the placeholder (the alt text). In my case this is something like 134 x 20.

Right now I'm just checking if the photo's height is less than 150, and assuming that if so it is just alt text. But this is quite a hack, and it would break if a photo is less than 150 pixels tall (not likely in my particular case), or if the alt text is more than 150 pixels tall (could possibly happen on a small browser window).


Edit: For anyone wanting to see the code:

$(function()
{
  var REAL_WIDTH = $("#photo").width();
  var REAL_HEIGHT = $("#photo").height();

  $(window).resize(adjust_photo_size);
  adjust_photo_size();

  function adjust_photo_size()
  {
    if(REAL_HEIGHT < 150)
    {
      REAL_WIDTH = $("#photo").width();
      REAL_HEIGHT = $("#photo").height();
      if(REAL_HEIGHT < 150)
      {
        //image not loaded.. try again in a quarter-second
        setTimeout(adjust_photo_size, 250);
        return;
      }
    }

    var new_width = . . . ;
    var new_height = . . . ;

    $("#photo").width(Math.round(new_width));
    $("#photo").height(Math.round(new_height));
  }

});

Update: Thanks for the suggestions. There is a risk of the event not being fired if I set a callback for the $("#photo").load event, so I have defined an onLoad event directly on the image tag. For the record, here is the code I ended up going with:

<img id="photo"
     onload="photoLoaded();"
     src="a_really_big_file.jpg"
     alt="this is some alt text"
     title="this is some title text" />

Then in Javascript:

//This must be outside $() because it may get called first
var isPhotoLoaded = false;
function photoLoaded()
{
  isPhotoLoaded = true;
}

$(function()
{
  //Hides scrollbars, so we can resize properly.  Set with JS instead of
  //  CSS so that page doesn't break with JS disabled.
  $("body").css("overflow", "hidden");

  var REAL_WIDTH = -1;
  var REAL_HEIGHT = -1;

  $(window).resize(adjust_photo_size);
  adjust_photo_size();

  function adjust_photo_size()
  {
    if(!isPhotoLoaded)
    {
      //image not loaded.. try again in a quarter-second
      setTimeout(adjust_photo_size, 250);
      return;
    }
    else if(REAL_WIDTH < 0)
    {
      //first time in this function since photo loaded
      REAL_WIDTH = $("#photo").width();
      REAL_HEIGHT = $("#photo").height();
    }

    var new_width = . . . ;
    var new_height = . . . ;

    $("#photo").width(Math.round(new_width));
    $("#photo").height(Math.round(new_height));
  }

});
Kip
  • 107,154
  • 87
  • 232
  • 265
  • 1
    This is so old and you've already accepted an answer, so I will just comment here. Why can't you use the jQuery plugin 'onImagesLoad'? And also, jQuery or not, what is wrong with setting `max-width` and `max-height` in the image styling with Javascript? You then avoid ALL of the code you've needed to write by setting max width/height to the size of the viewport width/height. –  Jan 22 '10 at 21:13
  • @jon.wd7: i'm not familiar with that plugin, and it may not have been around way back in '08. as for max-width and max-height, two things: 1) they aren't supported in IE (well I'm not sure about IE 8); 2) if the viewport changes size (window is resized, etc.) then i'd still need javascript to change the max-width/max-height (although i may not if i used "100%" rather than a pixel measurement) – Kip Jan 22 '10 at 21:27
  • It's definitely supported in IE7 and IE8, according to quirksmode.org. So I'm not sure of your issue. I personally don't support IE6 for small things like this, so what they will see is the same thing a user without JS enabled would see. And of course you would need to reset `max-width` and `max-height` on resize. But that's quite an easy task, a one-liner using `.resize()` in fact. –  Jan 22 '10 at 21:31
  • 3
    The [complete](http://www.w3schools.com/jsref/prop_img_complete.asp) property gives the imperative way of knowing whether the image has been loaded. – BorisOkunskiy Sep 23 '13 at 17:56
  • @incarnate I disagree. Because the image element's `complete` property is not clearly supported: unable to find a reliable source of about browser support. The HTML5 spec is the only one mentioning it & it is still in a draft version at the time of writting. see the only spec regarding the "complete" property http://www.w3.org/html/wg/drafts/html/master/embedded-content.html#dom-img-complete and Mozilla shows support for the "complete" property as unknown http://developer.mozilla.org/en/docs/Web/API/HTMLImageElement – Adriano Nov 14 '14 at 15:36
  • `imagesloaded` javascript library does an excellent job at this. See http://stackoverflow.com/questions/1977871/check-if-an-image-is-loaded-no-errors-in-javascript/19959809#19959809 – Adriano Nov 14 '14 at 15:38
  • 1
    @Adrien, Mozilla now [shows](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement) complete as supported by all major browsers, except for Andoid (unknown). – Oliver Bock Dec 08 '15 at 22:43

15 Answers15

33

Either add an event listener, or have the image announce itself with onload. Then figure out the dimensions from there.

<img id="photo"
     onload='loaded(this.id)'
     src="a_really_big_file.jpg"
     alt="this is some alt text"
     title="this is some title text" />
Diodeus - James MacFarlane
  • 112,730
  • 33
  • 157
  • 176
  • According to spec, onload is only an event for the body and frameset elements; that said, I think this works in IE, but I don't know that it works in any other browsers or that it is something you can assume...I was just looking into this a couple of weeks ago... – Jason Bunting Nov 05 '08 at 06:06
  • I have tested it in IE6, IE7, FF3, Opera9, Safair (Windows Beta), and Chrome (Windows Beta). – Kip Nov 05 '08 at 14:48
  • 10
    Messing behavior and content is discouraged. Events should be applied using Javascript, not in HTML. – dionyziz Dec 14 '12 at 08:25
  • 10
    His comment IS relevant since we are not stuck in 2008. People still read this, and even tho it wasn't considered bad practice in 08, you dont want people to think its good practice now. – Spoeken Mar 05 '13 at 12:28
  • so what is the better solution or best practice in 2013 now.... @MathiasMadsenStav – Hitesh Aug 30 '13 at 12:37
  • i think someone should give better answer for this which will help others, I know that question has already got the right answer but i am hoping their could be better answer without messing behavior and content using java script or jquery :):)...just a thought.....@dionyziz – Hitesh Aug 30 '13 at 12:40
  • 3
    document.querySelector("img").addEventListener("load", function() { alert('onload!'); }); – Frank Schwieterman Nov 27 '13 at 22:19
14

Using the jquery data store you can define a 'loaded' state.

<img id="myimage" onload="$(this).data('loaded', 'loaded');" src="lolcats.jpg" />

Then elsewhere you can do:

if ($('#myimage').data('loaded')) {
    // loaded, so do stuff
}
Mike Fogel
  • 3,127
  • 28
  • 22
  • 2
    Use `jQuery(this)` instead of `$(this)` for better compatibility with other libraries(jquery doesn't own the $), especially when you have JavaScript in html. – John Magnolia May 16 '13 at 13:30
  • There's a built in property for this: [`image.complete`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/complete). Don't use inline event handlers and don't use data attributes. – I wrestled a bear once. Apr 05 '22 at 15:33
11

The right answer, is to use event.special.load

It is possible that the load event will not be triggered if the image is loaded from the browser cache. To account for this possibility, we can use a special load event that fires immediately if the image is ready. event.special.load is currently available as a plugin.

Per the docs on .load()

Evan Carroll
  • 78,363
  • 46
  • 261
  • 468
  • 2
    I was going to add an answer with a suggestion like this, glad I saw this answer before coding it. Basically, your handlers needs to first check if the image is loaded before setting the handler. If it's already loaded, fire the callback immediately. It's what `$.ready` does. I was going to suggest just checking the width, but that has its problems, I really like that solution. – Ruan Mendes Mar 12 '13 at 19:11
5

You want to do what Allain said, however be aware that sometimes the image loads before dom ready, which means your load handler won't fire. The best way is to do as Allain says, but set the src of the image with javascript after attaching the load hander. This way you can guarantee that it fires.

In terms of accessibility, will your site still work for people without javascript? You may want to give the img tag the correct src, attach you dom ready handler to run your js: clear the image src (give it a fixed with and height with css to prevent the page flickering), then set your img load handler, then reset the src to the correct file. This way you cover all bases :)

Andrew Bullock
  • 36,616
  • 34
  • 155
  • 231
  • The site still works for people without Javascript, they just have to scroll to see all of the image. – Kip Nov 05 '08 at 14:49
4

As per one of the recent comments to your original question

$(function() {

  $(window).resize(adjust_photo_size);
  adjust_photo_size();

  function adjust_photo_size()  {
    if (!$("#photo").get(0).complete) {
       $("#photo").load(function() {
          adjust_photo_size();
       });
    } else {
      ... 
    }
});

Warning This answer could cause a serious loop in ie8 and lower, because img.complete is not always properly set by the browser. If you must support ie8, use a flag to remember the image is loaded.

commonpike
  • 10,499
  • 4
  • 65
  • 58
  • Hold it. This answer could cause a serious loop in ie8 and lower, because img.complete is not always properly set by the browser. If you must support ie8, use a flag to remember the image is loaded. – commonpike Jul 28 '14 at 11:30
2

There's a jQuery plugin called "imagesLoaded" that provides a cross-browser compatible method to check if an element's image(s) have been loaded.

Site: https://github.com/desandro/imagesloaded/

Usage for a container that has many images inside:

$('container').imagesLoaded(function(){
 console.log("I loaded!");
})

The plugin is great:

  1. works for checking a container with many images inside
  2. works for check an img to see if it has loaded
jay
  • 12,066
  • 16
  • 64
  • 103
  • seems kinda heavy 5KB minified only to check img loaded. Should be an easier lighter method available... – cdalxndr Apr 24 '20 at 08:08
2

I found this worked for me

document.querySelector("img").addEventListener("load", function() { alert('onload!'); });

Credit goes totaly to Frank Schwieterman, who commented on accepted answer. I had to put this here, it's too valuable...

Armand
  • 2,611
  • 2
  • 24
  • 39
2

Try something like:

$("#photo").load(function() {
    alert("Hello from Image");
});
Allain Lalonde
  • 91,574
  • 70
  • 187
  • 238
  • 6
    Thanks. However, there is a possibility that the image has already been loaded before this happens, in which case the load event wouldn't be fired. – Kip Nov 05 '08 at 14:47
  • How about a script tag setting this event right after the image tag? The reason to use ready is to make sure the entire document is loaded, which is unnecessary in this case, right? – Jason Goemaat Sep 28 '10 at 22:02
1

This function checks if an image is loaded based on having measurable dimensions. This technique is useful if your script is executing after some of the images have already been loaded.

imageLoaded = function(node) {
    var w = 'undefined' != typeof node.clientWidth ? node.clientWidth : node.offsetWidth;
    var h = 'undefined' != typeof node.clientHeight ? node.clientHeight : node.offsetHeight;
    return w+h > 0 ? true : false;
};
Ralph Ritoch
  • 3,260
  • 27
  • 37
  • I discovered this solution may require that there be no margin or padding on the image, and that alt text of an empty string is provided. In most cases though, it works as intended. It will only fail if the size of the unloaded image is larger than zero. – Ralph Ritoch Aug 10 '13 at 09:42
1

Any comments on this one?

...

doShow = function(){
  if($('#img_id').attr('complete')){
    alert('Image is loaded!');
  } else {
    window.setTimeout('doShow()',100);
  }
};

$('#img_id').attr('src','image.jpg');

doShow();

...

Seems like works everywhere...

KJYe.Name
  • 16,969
  • 5
  • 48
  • 63
Jay
  • 27
  • 1
1

I just created a jQuery function to load an image using jQuerys Deferred Object which makes it very easy to react on load/error event:

$.fn.extend({
    loadImg: function(url, timeout) {
        // init deferred object
        var defer = $.Deferred(),
            $img = this,
            img = $img.get(0),
            timer = null;

        // define load and error events BEFORE setting the src
        // otherwise IE might fire the event before listening to it
        $img.load(function(e) {
            var that = this;
            // defer this check in order to let IE catch the right image size
            window.setTimeout(function() {
                // make sure the width and height are > 0
                ((that.width > 0 && that.height > 0) ? 
                    defer.resolveWith : 
                    defer.rejectWith)($img);
            }, 1);
        }).error(function(e) {
            defer.rejectWith($img);
        });

        // start loading the image
        img.src = url;

        // check if it's already in the cache
        if (img.complete) {
            defer.resolveWith($img);
        } else if (0 !== timeout) {
            // add a timeout, by default 15 seconds
            timer = window.setTimeout(function() {
                defer.rejectWith($img);
            }, timeout || 15000);
        }

        // return the promise of the deferred object
        return defer.promise().always(function() {
            // stop the timeout timer
            window.clearTimeout(timer);
            timer = null;
            // unbind the load and error event
            this.off("load error");
        });
    }
});

Usage:

var image = $('<img />').loadImg('http://www.google.com/intl/en_com/images/srpr/logo3w.png')
.done(function() {
    alert('image loaded');
    $('body').append(this);
}).fail(function(){
    alert('image failed');
});

See it working at: http://jsfiddle.net/roberkules/AdWZj/

roberkules
  • 6,557
  • 2
  • 44
  • 52
  • That sounds wonderful. Can you please give an exact example of when your code is preferable to img.onload and img.onerror? Is your code only meant to handle the seeming shortcomings of jQuery error handling I was just informed about: that error does not trigger in IE if there is no file extension? – mplungjan Jun 12 '12 at 05:44
  • interesting idea. so knowing when image done its easy. But, what if the imaged couldn't be loaded. I was searching for a way to know if the loading the image has failed. and you gave me the idea of timeout onloading it. +1 for that – oak Feb 07 '14 at 08:15
  • 1
    @oak the timeout should be only a fallback. most browsers should fire an `error` event which is handled by `.error(function(e) { defer.rejectWith($img); })`. If you look at the jsFiddle you will see that the `http://www.google.com/abc.png not found` text is shown immediately in the results pane (not waiting for a timeout, because the error event kicked in) – roberkules Feb 07 '14 at 15:38
  • note that for jquery to raise .error two things should be 1. the handle but be set before the error raised. 2. .error handles only http protocol and not file:// so you wont get error there.. – oak Feb 10 '14 at 14:10
  • in my code example the success/error handler are set before assigning the src attribute exactly for that reason. about the `file://` restriction - I never needed to link images using the file protocol and I don't think there's much need. – roberkules Feb 10 '14 at 15:03
0

We developed a page where it loaded a number of images and then performed other functions only after the image was loaded. It was a busy site that generated a lot of traffic. It seems that the following simple script worked on practically all browsers:

$(elem).onload = function() {
    doSomething();
}

BUT THIS IS A POTENTIAL ISSUE FOR IE9!

The ONLY browser we had reported issues on is IE9. Are we not surprised? It seems that the best way to solve the issue there is to not assign a src to the image until AFTER the onload function has been defined, like so:

$(elem).onload = function() {
    doSomething();
}
$(elem).attr('src','theimage.png');

It seems that IE 9 will sometimes not throw the onload event for whatever reason. Other solutions on this page (such as the one from Evan Carroll, for example) still did not work. Logically, that checked if the load state was already successful and triggered the function and if it wasn't, then set the onload handler, but even when you do that we demonstrated in testing that the image could load between those two lines of js thereby appearing not loaded to the first line and then loading before the onload handler is set.

We found that the best way to get what you want is to not define the image's src until you have set the onload event trigger.

We only just recently stopped supporting IE8 so I can't speak for versions prior to IE9, otherwise, out of all the other browsers that were used on the site -- IE10 and 11 as well as Firefox, Chrome, Opera, Safari and whatever mobile browser people were using -- setting the src before assigning the onload handler was not even an issue.

Octopus
  • 8,075
  • 5
  • 46
  • 66
0

May I suggest a pure CSS solution altogether?

Just have a Div that you want to show the image in. Set the image as background. Then have the property background-size: cover or background-size: contain depending on how you want it.

cover will crop the image until smaller sides cover the box. contain will keep the entire image inside the div, leaving you with spaces on sides.

Check the snippet below.

div {
  height: 300px;
  width: 300px;
  border: 3px dashed grey;
  background-position: center;
  background-repeat: no-repeat;
}

.cover-image {
  background-size: cover;
}

.contain-image {
  background-size: contain;
}
<div class="cover-image" style="background-image:url(https://assets1.ignimgs.com/2019/04/25/avengers-endgame-1280y-1556226255823_1280w.jpg)">
</div>
<br/>
<div class="contain-image" style="background-image:url(https://assets1.ignimgs.com/2019/04/25/avengers-endgame-1280y-1556226255823_1280w.jpg)">
</div>
arunwithasmile
  • 300
  • 4
  • 16
0

I find that this simple solution works best for me:

        function setEqualHeight(a, b) {
            if (!$(a).height()) {
                return window.setTimeout(function(){ setEqualHeight(a, b); }, 1000);
            }
            $(b).height($(a).height());
        }

        $(document).ready(function() {
            setEqualHeight('#image', '#description');
            $(window).resize(function(){setEqualHeight('#image', '#description')});
        });
    </script>
0

image.complete might be another option https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/complete

  • 1
    While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. [Answers that are little more than a link may be deleted](https://stackoverflow.com/help/deleted-answers) – Rajesh Pandya Jun 11 '21 at 06:01