如何使用 Javascript/jQuery 确定图像是否已加载?

我正在编写一些 Javascript 来调整大图像的大小以适应用户的浏览器窗口。(不幸的是,我不能控制源图像的大小。)

所以类似这样的东西会出现在 HTML 中:

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

有没有办法让我确定 img标签中的 src图像是否已被下载?

我需要这个,因为我遇到了一个问题,如果 $(document).ready()是在浏览器加载图像之前执行的。$("#photo").width()$("#photo").height()将返回占位符的大小(替换文本)。在我的例子中,它大概是134x20。

现在我只是检查照片的高度是否小于150,并假设如果是这样,它只是替换文本。但是,这是一个相当黑客,它会打破,如果一张照片不到150像素高(不太可能在我的特殊情况下) ,或如果替换文本超过150像素高(可能发生在一个小的浏览器窗口)。


编辑: 对于任何想看代码的人:

$(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));
}


});

更新 : 感谢您的建议。如果我为 $("#photo").load事件设置了回调,那么就有不触发事件的风险,所以我直接在图像标记上定义了一个 onLoad 事件。郑重声明,以下是我最后使用的代码:

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

然后在 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));
}


});
137051 次浏览

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" />

Try something like:

$("#photo").load(function() {
alert("Hello from Image");
});

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 :)

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...

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()

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
}

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/

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;
};

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

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.

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...

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.

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>

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>