jQuery 图片加载回调(即使图片已缓存)

305

13
自从您提问以来,情况有所变化。该破损的插件已被移至代码片段(gist),后来又移到了一个包含小型可用插件jQuery.imagesLoaded的代码库中。它们解决了所有浏览器的小问题 - Lode
上述提到的 JQuery 库对我来说运行得非常好。谢谢! - plang
14个回答

592
如果src已经被设置,那么在绑定事件处理程序之前,事件会在缓存的情况下触发。为了解决这个问题,你可以通过循环检查和基于.complete触发事件来修复它,如下所示:
$("img").one("load", function() {
  // do stuff
}).each(function() {
  if(this.complete) {
      $(this).load(); // For jQuery < 3.0 
      // $(this).trigger('load'); // For jQuery >= 3.0 
  }
});

注意从.bind().one()的更改,以便事件处理程序不会运行两次。


8
你的解决方案对我非常有效,但我想要理解一件事情:当这段代码“if(this.complete)”运行时,它会在图像内容加载之后还是之前执行?因为我从你的 .each 中了解到你正在循环所有的 $("img"),而图片内容可能为空,加载操作也就不会发生。嗯,我觉得我缺少了一些信息,如果你能描述一下正在发生的事情,那就太好了。谢谢! - Amr Elgarhy
37
自您回答以来情况发生了变化。现在有一个小型的工作插件 jQuery.imagesLoaded。它们可以修复所有浏览器小问题 - Lode
3
我会在'load'函数内添加一个setTimeout(function(){//做事情},0)...这个0会让浏览器系统(DOM)再多进行一次更新,确保一切都已经正确更新。 - dsdsdsdsd
14
@Lode - 这是一个非常庞大的插件,无论从任何角度来看都不算小!! - vsync
6
自 JQuery 3 版本起,方法 .load() 不会触发事件且只有一个必需参数,请改用 .trigger("load") - ForceUser
显示剩余14条评论

55

我可以建议您将其重新加载到非DOM图像对象中吗?如果已缓存,这将不需要任何时间,并且仍将触发onload事件。如果未缓存,则在图像加载完成时将触发onload事件,这应该与DOM版本的图像完成加载的时间相同。

Javascript:

$(document).ready(function() {
    var tmpImg = new Image() ;
    tmpImg.src = $('#img').attr('src') ;
    tmpImg.onload = function() {
        // Run onload code.
    } ;
}) ;

已更新(以处理多个图像,并具有正确排序的onload附件):

$(document).ready(function() {
    var imageLoaded = function() {
        // Run onload code.
    }
    $('#img').each(function() {
        var tmpImg = new Image() ;
        tmpImg.onload = imageLoaded ;
        tmpImg.src = $(this).attr('src') ;
    }) ;
}) ;

1
你应该在添加之前尝试附加 load 处理程序,此外这种方法只适用于一张图片,而 OP 的代码适用于多张图片 :) - Nick Craver
谢谢,我已经添加了更新版本,希望解决这两个问题。 - Gus
#img 是一个 ID,而不是元素选择器 :) 此外,this.src 可以工作,不需要在不必要的情况下使用 jQuery :) 但在任何情况下创建另一个图像似乎都有些过度设计。 - Nick Craver
它应该是$('img'),但解决方案在2015年仍然很棒。 - Dimag Kharab
此外,如果要在多个图片的情况下对每个“imageLoaded”中的DOM元素进行操作,则可以使用“tmp.onload = imageLoaded.bind(this)” - blisstdev

41

我的简单解决方案,不需要任何外部插件,对于常见情况应该已经足够了:

/**
 * Trigger a callback when the selected images are loaded:
 * @param {String} selector
 * @param {Function} callback
  */
var onImgLoad = function(selector, callback){
    $(selector).each(function(){
        if (this.complete || /*for IE 10-*/ $(this).height() > 0) {
            callback.apply(this);
        }
        else {
            $(this).on('load', function(){
                callback.apply(this);
            });
        }
    });
};

使用方法如下:

onImgLoad('img', function(){
    // do stuff
});
例如,要在加载时淡入图像,您可以执行以下操作:
$(document).ready(function() {
  $('img').hide();
  $('img').fadeIn(1000);
});
$('img').hide();
onImgLoad('img', function(){
    $(this).fadeIn(700);
});

或者,如果你更喜欢像 jQuery 插件一样的方法:

/**
 * Trigger a callback when 'this' image is loaded:
 * @param {Function} callback
 */
(function($){
    $.fn.imgLoad = function(callback) {
        return this.each(function() {
            if (callback) {
                if (this.complete || /*for IE 10-*/ $(this).height() > 0) {
                    callback.apply(this);
                }
                else {
                    $(this).on('load', function(){
                        callback.apply(this);
                    });
                }
            }
        });
    };
})(jQuery);

并且以这种方式使用它:

$('img').imgLoad(function(){
    // do stuff
});
例如:
$('img').hide().imgLoad(function(){
    $(this).fadeIn(700);
});

那如果我的图片已经通过CSS设置了大小呢? - Simon_Weaver
设置 widthmax-height 并保留 height:auto,通常只有在需要拉伸图像时才需要同时设置宽度和高度;对于更强大的解决方案,我建议使用像 ImagesLoaded 这样的插件。 - guari
1
是的,那是一个打错字,jsdoc是正确的,我已经更正了行示例。 - guari
1
这个使用$.fn.imgLoad的解决方案可以跨浏览器工作。修复它会更好:&& /for IE 10-/ this.naturalHeight > 0 这一行不会考虑CSS样式,因为图片可能被阻止但仍有高度。在IE10中工作。 - Patryk
1
这确实存在(小)竞态条件。图像可以在不同的线程中加载到脚本中,因此可能会在完成检查和附加onload事件之间加载,如果是这种情况,则什么也不会发生。通常JS与渲染同步,因此您不必担心竞争条件,但这是一个例外。https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-complete - Mog0
显示剩余2条评论

24

你真的需要使用jQuery吗?你也可以直接将onload事件附加到你的图像上;

<img src="/path/to/image.jpg" onload="doStuff(this);" />
它会在图片被加载后每次触发,不论是否从缓存中获得。

5
没有内联 JavaScript 更加干净。 - hazelnut
1
嗨,Bjorn,当图像从缓存中第二次加载时,onload处理程序会触发吗? - SexyBeast
1
@Cupidvogel 不,它不会。 - Ads
3
老兄,你救了我的命,我试过至少十几种其他方法,而只有你的方法真正奏效了。我认真考虑再创建10个账户,给你的答案投10次赞。真心感谢! - Carlo
1
我从来不理解人们对内联JS的厌恶。图像加载与页面加载过程的其余部分分别进行,因此这是获取有关图像加载完成的即时反馈的唯一有效解决方案。但是,如果有人需要等待jQuery加载,而这发生在稍后,那么他们将不得不不使用这里的简单解决方案,并使用其他解决方案之一(Piotr的可能是最佳选择,因为它不依赖于伪黑客,但guari的height()检查也可能很重要)。处理队列也可能很有用。 - CubicleSoft
@Ads 是的,它会并且已经实现了。 - Sajjan Sarkar

17

您还可以使用此代码,并支持加载错误:

$("img").on('load', function() {
  // do stuff on success
})
.on('error', function() {
  // do stuff on smth wrong (error 404, etc.)
})
.each(function() {
    if(this.complete) {
      $(this).load();
    } else if(this.error) {
      $(this).error();
    }
});

1
这个对我来说效果最好。我认为关键是先设置load处理程序,然后在each函数中稍后调用它。 - GreenRaccoon23
1
使用以下代码设置图像:''var img = new Image(); img.src = sosImgURL; $(img).on("load", function () {'' - imdadhusen

13

我自己也遇到了这个问题,在各处搜寻解决方案,不想清空缓存或下载插件。

由于我没有立即看到这个主题,所以我找到了其他的解决方法,这是一个有趣的修复方法,值得在这里发布:

$('.image').load(function(){
    // stuff
}).attr('src', 'new_src');

实际上,我是从这里的评论中得到了这个想法:http://www.witheringtree.com/2009/05/image-load-event-binding-with-ie-using-jquery/

我不知道为什么它起作用,但我已经在IE7上测试过了,在以前会出问题的地方现在已经可以工作。

希望对你有所帮助,

编辑

被接受的答案实际上解释了原因:

  

如果src已经设置,则事件将在您绑定事件处理程序之前在缓存中触发。


4
我已经在许多浏览器上测试过了,没有发现它在任何地方出现问题。我使用 $image.load(function(){ something(); }).attr('src', $image.attr('src')); 重置了原始源。 - Maurice
我发现这种方法在iOS上不起作用,至少对于Safari/601.1和Safari/602.1是如此。 - cronfy
查明原因并知道使用的是哪个浏览器会很棒。 - Sammaye

5

这正是我一直在寻找的。服务器只传来了一张图片,我想要预加载它然后进行服务。而不是像其他答案那样选择所有图片。很好的解决方案。 - Kai Qing

5
通过使用jQuery生成具有图像src的新图像,并将load方法直接分配给该图像,当jQuery完成生成新图像时,load方法会成功被调用。在IE 8、9和10中,这对我起作用。
$('<img />', {
    "src": $("#img").attr("src")
}).load(function(){
    // Do something
});

对于那些使用Ruby on Rails,进行远程请求,使用__rjs.erb模板的人来说,这是我找到的唯一解决方案。因为在js.erb中使用partials,它会失去绑定($(“#img”).on(“load”)...而对于图像来说不可能委托“on”方法:$(“body”).on(“load”、“#img”,function()... - Albert Català

4

对GUS示例的修改:

$(document).ready(function() {
    var tmpImg = new Image() ;
    tmpImg.onload = function() {
        // Run onload code.
    } ;

tmpImg.src = $('#img').attr('src');
})

在onload之前和之后设置源。

和@Gus的答案一样,这对于多个图像是行不通的...在附加onload处理程序之前没有必要设置src,之后一次就足够了。 - Nick Craver

3

在img对象被定义之后,单独一行添加src参数即可。这样可以欺骗IE触发load事件。虽然不太美观,但这是我目前发现的最简单的解决方法。

jQuery('<img/>', {
    src: url,
    id: 'whatever'
})
.load(function() {
})
.appendTo('#someelement');
$('#whatever').attr('src', url); // trigger .load on IE

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