独立于浏览器的方式来检测图像何时已加载

55
在IE中,你可以使用onreadystatechange。虽然有onload,但我读到了可怕的事情。jQuery使用"ready"很好地封装了DOM的加载事件。我似乎只是不知道另一个很好的库实现图像加载的方法。上下文是我通过服务器回调动态生成图像,可能需要一些时间下载。在仅限于IE的代码中,我设置img元素的src,然后当onreadystatechange事件以"complete"状态触发时,将其添加到DOM中,以便用户看到它。我会很高兴使用"native" JavaScript解决方案,或者指向执行此工作的库的指针。有这么多的库存在,我肯定只是不知道正确的库。话虽如此,我们已经是jQuery用户,所以我不想添加一个非常大的库来获取此功能。

我不是很明白 - 你是想要一个单独的纯JS解决方案来检测图像何时加载完成,还是在寻找一个替代jQuery用于检测图像何时加载完成的库? - Dan Lew
我两者都可以接受。我不会放弃jQuery,因为我在其他地方使用它;只是表达了我对一个我不熟悉的库已经解决了这个问题的怀疑。 - Sebastian Good
别忘了告诉我们你最终做了什么! - Jason Bunting
1
经过大量阅读,最佳答案似乎是使用imagesLoaded Javascript库,它具有以下特点:MIT许可证,在Github上拥有庞大的社区,成熟的项目(非常古老,自2010年以来一直活跃),7Kb压缩后(轻巧!)且无依赖关系。相关信息请参见https://dev59.com/FnI-5IYBdhLWcg3wMFQf - Adriano
9个回答

47

注意:此文于2010年编写,当时主流浏览器为IE 8/9 beta、Firefox 3.x和Chrome 4.x。请仅将其用于研究目的,我怀疑您无法将其复制/粘贴到现代浏览器中并顺利运行。

警告:现在是2017年,我偶尔还会收到关于此问题的提问,请仅将其用于研究目的。我目前不知道如何检测图像加载状态,但可能有更优雅的方法来实现它...至少我非常希望有。 我强烈建议不要在生产环境中使用我的代码而没有进行更多的研究。

警告第二部分 Electric Boogaloo:现在是2019年,大多数jQuery功能已内置于原生JS中。如果您仍在使用它,也许是时候停止并考虑前往MDN了解一些新的有趣的原生JS内容。


我有点晚到这个聚会,也许这个答案能帮助其他人……

如果您正在使用jQuery,请不要使用自带的事件处理程序(例如onclick/onmouseover等),最好完全停止使用它们。请使用他们在API中提供的事件方法。


这将在图像附加到页面主体之前进行警报,因为加载事件是在将图像加载到内存时触发的。它正在执行您要求它执行的操作:创建一个src为test.jpg的图像,在test.jpg加载完成后发出警报,然后将其附加到主体上。

var img = $('<img src="test.jpg" />');
img.load(function() {
    alert('Image Loaded');
});
$('body').append(img);

在将图像插入主体后,这将触发警报并执行您指定的操作:创建图像,设置事件(未设置src,因此尚未加载),将图像附加到主体(仍未设置src),现在设置src...现在已加载图像,因此触发事件。

var img = $('<img />');
img.load(function() {
    alert('Image Loaded');
});
$('body').append(img);
$img.attr('src','test.jpg');

当然,你也可以添加错误处理程序并使用bind()合并一些事件。
var img = $('<img />');
img.bind({
    load: function() {
        alert('Image loaded.');
    },
    error: function() {
        alert('Error thrown, image didn\'t load, probably a 404.');
    }
});
$('body').append(img);
img.attr('src','test.jpg');

根据@ChrisKempen的要求...

以下是一种非事件驱动的方法,用于在DOM加载后确定图像是否损坏。这段代码是从StereoChrome的一篇文章中派生出来的,它使用naturalWidth、naturalHeight和complete属性来确定图像是否存在。

$('img').each(function() {
    if (this.naturalWidth === 0 || this.naturalHeight === 0 || this.complete === false) {
        alert('broken image');
    }
});

12
警告:来自jQuery文档:使用load事件与图像时的注意事项:开发人员尝试使用.load()快捷方式解决的常见问题是在图像(或图像集合)完全加载后执行函数。 有几个已知的注意事项应该注意。 这些包括:它在不同浏览器中不能一致可靠地工作 如果将图像src设置为之前的相同src,则在WebKit中不会正确触发 它无法正确地向上传递DOM树 对于已经存在于浏览器缓存中的图像,可能会停止触发 - Ben
1
我来晚了,但如果图像已经被添加,你需要检查它们是否已经加载,该怎么办?谢谢任何想法/建议! :) - Chris Kempen
5
@ChrisKempen -- 我在我的上一篇帖子中为您添加了一种非事件驱动的方法,请同时查看我代码来源的原始文章。 - Paul J
1
@paulj 注意,naturalWidthnaturalHeight仅兼容IE9+,请参考以下链接以获取跨浏览器支持:http://stackoverflow.com/a/7869044/759452 和 http://www.jacklmoore.com/notes/naturalwidth-and-naturalheight-in-ie/ ,也可以参考http://css-tricks.com/snippets/jquery/get-an-images-native-width/。 - Adriano

11

根据W3C规范, 只有BODY和FRAMESET元素提供了"onload"事件来附加。一些浏览器可能仍然支持它,但请注意,这不是实现W3C规范所必需的。

虽然这可能与讨论相关,但并不一定是你需要的答案,以下是相关讨论:

在Internet Explorer中,当图像处于缓存中时,Image.onload事件不会触发


还有一些可能与您的需求相关的内容,尽管它可能并不相关,就是关于为任何动态加载的DOM元素支持合成"onload"事件的信息:

我该如何确定一个动态创建的DOM元素是否已被添加到DOM中?


当然,我从未说过他不能依赖它,只是确保他知道规范 - 忽略规范将自食其果。 - Jason Bunting
@Christoph - 在你的近视中,你忘记了两件事:1)我指出这可能不是“与本讨论相关的”,但更重要的是,2)其他人可能会在某一天登陆到这个页面,因为他们遇到了我链接的问题,并且通过搜索SO找到了这里。如果我发布了一个关于erlang元组问题的链接,我可以理解你会感到不满,但我认为我的评论是相关的。 - Jason Bunting
好了,伙计们冷静点。由于我自己将元素添加到DOM中,所以我对额外的链接不是很感兴趣。我想这里的暗示是在IE中应该使用onreadystatechange,在其他地方应该使用onload,虽然这不是标准,但得到了广泛支持? - Sebastian Good
@Sebastian:没错,对于一个无关紧要的问题进行一场激烈的争论也没什么不好的。 @Jason:“你打架的样子就像个挤奶的农民!” - Christoph
@Christoph:“多贴切啊,你打架的样子就像头牛。” :) - Jason Bunting
显示剩余4条评论

6
我发现唯一可靠的方法是在客户端完成所有操作,就像这样...
var img = new Image();
img.onload = function() {
  alert('Done!');
}
img.src = '/images/myImage.jpg';

1
尽管有些浏览器支持它,但“onload”事件并未得到W3C标准支持,因此不是所有浏览器都需要或必须支持它。 - Jason Bunting

3

我认为onload应该能够正常工作。您是否必须为图像生成标记,还是可以静态添加它们? 如果是后者,我建议采取以下措施:

<img src="foo.png" class="classNeededToHideImage" onload="makeVisible(this)">

或者,您可以使用window.onload使所有图像立即可见 - window.onload在所有外部资源加载完成后触发。

如果要动态添加图像,则首先必须等待DOM准备好进行修改,即使用jQuery或为不支持其本地实现的浏览器实现自己的DOMContentLoaded hack。

然后,您创建图像对象,分配onload侦听器使它们可见和/或将它们添加到文档中。

更好的方法(但稍微复杂一些)是在脚本执行时立即创建并开始加载图像。在DOMContentLoaded上,您将需要检查每个图像的complete以决定是立即将其添加到文档中还是等待图像的onload侦听器触发。


1
尽管一些浏览器支持它,但“onload”事件并未得到W3C标准的支持,因此并非所有浏览器都需要或必须支持它。 - Jason Bunting
嗯,当“几个浏览器”似乎是自从NN4以来的每个主流浏览器时,可以合理地假设该功能不会很快消失。尽管令人遗憾,但W3C规范很少反映Web开发人员面临的现实... - Christoph
我知道这一点,但是总是好的去注意规范说了什么。即使没有其他原因,也要了解你所指出的是它们脱离现实需要更新。顺便问一下,“js-hacks”是什么,为什么需要?我从你的个人资料中发现你创建了它,但很难发现它是什么,因为它的网站缺乏信息,我没有时间浏览所有文件找到这个内容。 - Jason Bunting
@Jason:js-hacks 是一个地方,可以存放独立脚本,以便我可以链接到最新版本;它们中的大部分基本上是无用的或只是为了好玩,但也有一些是非常好的,当您不想使用完整框架时,这些很好用;问题是,我太懒了,连写文档都不愿意,即使有些脚本可能值得向更广泛的公众暴露(例如http://mercurial.intuxication.org/hg/js-hacks/raw-file/tip/capture.js非常好,改变了我的事件处理方式...) - Christoph
@Christoph:啊,好的 - 我不确定,以为它可能是某种框架。我是 MochiKit 的忠实用户(已经使用了三年),最近三个月开始使用 jQuery,但我喜欢 JavaScript 并且喜欢学习关于它的新东西,也许我会从你的脚本中获得一些有用的东西。谢谢。 - Jason Bunting

3

好的,我会尝试总结这里各种部分答案(包括我的)。

看起来答案是使用onload,它“被广泛支持”,尽管根据标准不是必需的。请注意,特别是对于IE浏览器,您必须在设置src属性之前设置onLoad,否则当从缓存加载图像时可能不会触发onload事件。无论如何,这似乎是最佳实践:在开始触发事件之前设置您的事件处理程序。如果不支持onload,则应假定该功能不可用。


1

如果我没记错的话,在JavaScript中,图像标签有onload和onerror事件。

这里有一段有趣的代码,可以隐藏损坏的图片。

在样式标签中:

img.failed{
   display:none;
}

使用img的onerror事件,我有一段JavaScript代码,它会将this.className设置为failed,如果出现错误,则会隐藏图像。

然而,我可能误解了您的目标。


1
虽然一些浏览器支持它,但 "onload" 事件并没有得到 W3C 标准的支持,因此并非所有浏览器都需要或必须支持它。 - Jason Bunting

1

HTML

<img id="myimage" src="http://farm4.static.flickr.com/3106/2819995451_23db3f25fe_t.jpg"/>​

脚本

setTimeout(function(){
    $('#myimage').attr('src','http://cdn.redmondpie.com/wp-content/uploads/2011/05/canarylogo.png').load(function(){
        alert('Main Image Loaded');

        $('#myimage').attr('src','image.jpg').bind({
            load: function(){
                alert('Image loaded.');
            },
            error: function(){
                alert('Image not loaded.');
            }
        });
    });
},1000);

JS Fiddle

http://jsfiddle.net/gerst20051/sLge3/


1

看起来答案是在IE中使用onreadystatechange,在其他浏览器中使用onLoad。如果两者都不可用,则执行其他操作。onLoad更可取,但是正如下面的一个答案所指出的那样,当从缓存加载图像时,IE不会触发它。


IE在从缓存加载时会触发onload事件 - 您只需要确保在设置src之前设置onload即可;Jason提到的问题是特定于GWT的,这在链接页面上有提到。 - Christoph
看起来你说得对——感谢你深入到评论链的末尾。现在我需要测试一下…… - Sebastian Good

0

使用数据URI是正确的方法。然而,使用SVG而不是GIF提供了更多的灵活性。您可以快速更改大小。只需在JavaScript控制台中运行以下命令:

var width = 1;
var height = 1;
'data:image/svg+xml;base64,' +
btoa(`<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}"/>`);

示例输出:

data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxIiBoZWlnaHQ9IjEiLz4=

我写了一个快速的CodePen实用程序,可以创建任意尺寸的透明SVG图像。它非常基础,如果您想要添加颜色或其他功能到图像中,您需要修改它。

与单像素图像相比,最大的优势是它允许保持纵横比。这对于动态大小的图像非常重要,因为布局通常在src更改为真实图像之前执行(如果这是预期目的)。在src更改之前,图像将是正方形的,这可能是不可取的。


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