如何使用Javascript/jQuery判断图片是否已加载?

88
我正在编写一些Javascript代码,将大图像调整大小以适应用户的浏览器窗口。(不幸的是,我无法控制源图像的大小。)
因此,在HTML中会有类似这样的内容:
<img id="photo"
     src="a_really_big_file.jpg"
     alt="this is some alt text"
     title="this is some title text" />

有没有办法让我确定标签中的src图像是否已下载?
我需要这个,因为如果在浏览器加载图片之前执行$(document).ready(),就会遇到问题。$("#photo").width()和$("#photo").height()将返回占位符(alt文本)的大小。在我的情况下,这是大约134 x 20。
现在我只是检查照片的高度是否小于150,并假设如果是,则只是alt文本。但这是相当粗略的方法,如果照片小于150像素高(在我特定的情况下不太可能),或者alt文本超过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));
  }

});

1
这已经很老了,而且你已经接受了一个答案,所以我只是在这里评论一下。为什么不能使用jQuery插件'onImagesLoad'?而且,无论是jQuery还是其他,使用JavaScript在图像样式中设置'max-width'和'max-height'有什么问题吗?然后,通过将最大宽度/高度设置为视口宽度/高度的大小,您可以避免编写所有代码。 - user136565
3
complete 属性提供了一种重要的方式来判断图片是否已经加载完成。 - BorisOkunskiy
@incarnate 我不同意。因为图像元素的 complete 属性并没有得到明确的支持:无法找到可靠的关于浏览器支持的来源。HTML5 规范是唯一提到它的,而且在撰写本文时仍处于草案版本。请参见关于 "complete" 属性的唯一规范 http://www.w3.org/html/wg/drafts/html/master/embedded-content.html#dom-img-complete,Mozilla 显示对 "complete" 属性的支持状态为未知 http://developer.mozilla.org/en/docs/Web/API/HTMLImageElement。 - Adriano
imagesloaded JavaScript库在这方面做得非常出色。请参见https://dev59.com/FnI-5IYBdhLWcg3wMFQf#19959809。 - Adriano
1
@Adrien,Mozilla现在显示完全支持所有主要浏览器,除了Andoid(未知)。 - Oliver Bock
显示剩余2条评论
15个回答

33

要么添加事件监听器,要么在图像加载完成后让其自行宣布。然后从那里计算出尺寸。

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

10
不鼓励混淆行为和内容。应该使用 Javascript 而不是 HTML 实现事件。 - dionyziz
10
他的评论很相关,因为我们并没有停留在2008年。人们仍然会阅读这篇内容,尽管在08年并不被认为是坏习惯,但现在你不希望人们认为它是好习惯。 - Spoeken
那么,2013年现在有什么更好的解决方案或最佳实践呢?@MathiasMadsenStav - Hitesh
我认为有人应该给出更好的答案,以帮助其他人。我知道这个问题已经得到了正确的答案,但我希望能够在不影响行为和内容的情况下使用JavaScript或jQuery提供更好的答案:)......只是一个想法.....@dionyziz - Hitesh
3
document.querySelector("img").addEventListener("load", function() { alert('onload!'); });翻译:当图像加载完成时,会触发指定元素的load事件,并执行其中的函数(本例中弹出' onload!'提示框)。 - Frank Schwieterman
显示剩余2条评论

14

通过使用jQuery 数据存储,您可以定义“已加载”状态。

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

那么在其他地方,您可以执行以下操作:

if ($('#myimage').data('loaded')) {
    // loaded, so do stuff
}

2
使用 jQuery(this) 而不是 $(this) 可以更好地与其他库(jQuery 不拥有 $)兼容,特别是当您在 HTML 中使用 JavaScript 时。 - John Magnolia
这个有一个内置属性:image.complete。不要使用内联事件处理程序,也不要使用数据属性。 - I wrestled a bear once.

11

正确的做法是使用 event.special.load

如果从浏览器缓存加载图片,则可能不会触发 load 事件。为了考虑到这种可能性,我们可以使用一个特殊的 load 事件,如果图片准备好就立即触发。目前, event.special.load 可以作为插件使用。

根据.load()文档


2
我本来想提出一个建议,就像这个答案一样,很高兴在编码之前看到了这个答案。基本上,您的处理程序需要首先检查图像是否已加载,然后再设置处理程序。如果它已经加载,立即触发回调。这就是 $ .ready 的作用。我原本想建议只检查宽度,但那有它的问题,我真的很喜欢那个解决方案。 - Ruan Mendes

5
你想执行Allain说的那样,但要注意有时图像会在dom准备好之前加载,这意味着你的load处理程序不会触发。最好的方式是按照Allain所说的做,但是在附加了load处理程序后,使用javascript设置图像的src。这样可以保证它被触发。
就可访问性而言,如果没有启用JavaScript,您的站点是否仍然能够正常工作?您可能需要为img标签提供正确的src,并将dom准备好处理程序附加到运行您的js上:清除图像src(使用CSS给它一个固定的宽度和高度以防止页面闪烁),然后设置您的img load处理程序,最后将src重置为正确的文件。这样您就可以覆盖所有基础 :)

该网站仍然适用于没有启用Javascript的用户,他们只需向下滚动页面即可查看所有图像。 - Kip

4
根据您原问题的最近一条评论,如下所述:
$(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 {
      ... 
    }
});

警告:此答案可能会在IE8及更低版本中引起严重循环,因为浏览器并不总是正确设置img.complete。如果必须支持IE8,请使用一个标识来记住图像已加载。


等一下。这个答案可能会在ie8及以下版本中引起严重的循环,因为img.complete并非总是由浏览器正确设置。如果您必须支持ie8,请使用一个标记来记住图像已加载。 - commonpike

2

可以尝试类似以下的代码:

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

6
谢谢。但是,有可能在此之前已经加载了图像,这种情况下,load事件就不会触发。 - Kip
那么在图像标签之后设置一个脚本标签如何?使用 ready 的原因是确保整个文档已加载,但在这种情况下是不必要的,对吧? - Jason Goemaat

2
有一个名为“imagesLoaded”的jQuery插件,它提供了一种跨浏览器兼容的方法来检查元素的图像是否已加载。
网站:https://github.com/desandro/imagesloaded/ 使用方法:适用于包含多个图像的容器。
$('container').imagesLoaded(function(){
 console.log("I loaded!");
})

这个插件非常好用:

  1. 可以检查包含多张图片的容器
  2. 可以检查图片是否已经加载完成

看起来有点沉重,仅为5KB压缩后的大小,只是为了检查图像是否已加载。应该有一种更简单、更轻量级的方法可用... - cdalxndr

2

我发现这对我很有效

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

全部功劳归于Frank Schwieterman,他在接受的回答中发表了评论。我不得不把这个留在这里,因为它太有价值了...


1

我刚刚创建了一个jQuery函数,使用jQuery的Deferred对象来加载图像,这使得在加载/错误事件上反应非常容易:

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

使用方法:

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

在这里查看它的工作效果:http://jsfiddle.net/roberkules/AdWZj/


听起来很棒。您能否举一个确切的例子,说明何时使用您的代码比img.onload和img.onerror更好?您的代码是否只是用于处理我刚刚被告知的jQuery错误处理的表面缺陷:如果没有文件扩展名,则该错误在IE中不会触发? - mplungjan
有趣的想法。因此,知道图像何时完成很容易。但是,如果图像无法加载怎么办。我正在寻找一种方法来知道是否加载图像失败。你给了我在加载时设置超时的想法。+1 - oak
1
@oak 超时应该只是一种后备方案。大多数浏览器应该会触发一个“error”事件,由.error(function(e) { defer.rejectWith($img); })处理。如果您查看jsFiddle,您会看到“http://www.google.com/abc.png未找到”的文本立即显示在结果窗格中(不等待超时,因为错误事件已经触发)。 - roberkules
请注意,要使jQuery触发.error事件,必须满足以下两个条件:1.在引发错误之前必须设置处理程序。2..error仅处理HTTP协议,而不处理file://协议,因此您不会在那里收到错误。 - oak
在我的代码示例中,成功/错误处理程序是在分配src属性之前设置的,正是为了这个原因。关于file://限制-我从来没有需要使用文件协议链接图像,也不认为有太多需要。 - roberkules

1

对这个有任何评论吗?

...

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

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

doShow();

...

似乎在任何地方都能工作...


使用setTimeout是不好的编程实践。 - cdalxndr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接