jQuery:动态图像处理(等待加载)

4
我正在尝试编写一个插件,其中包含一个内置功能,在执行自身之前等待页面上的所有图像加载完成。如果通过AJAX拉取包含图像的HTML,则$(window).load()仅在页面的初始加载时起作用,但是它不适用于后来加载的图像。有没有好方法可以做到这一点,并且实现自包含在插件中?我已经尝试过循环使用$('img').complete,但我知道那行不通(图像永远不会加载,并且浏览器崩溃了“脚本执行时间过长”的错误)。访问我想要放置插件的页面的示例,请单击导航栏上的“more uses”部分:http://www.simplesli.de。您将看到图像幻灯片无法正确加载。我知道当前的代码不起作用,但只是为了占位,直到我找到解决方案。

编辑:尝试此解决方案,但它不起作用:

if($('.simpleSlide-slide img').size() > 0) {
    var loaded_materials = $('.simpleSlide-slide img').get();
} else {
    var loaded_materials = $('.simpleSlide-slide').get();
}

$(loaded_materials).live('load', function() {
         /* Image processing and loading code */
    });
4个回答

3
我认为您需要将一个函数绑定到全局ajaxSuccess事件,然后为页面上的每个图像绑定一个load事件。
$.ajaxSuccess(function(){
 $('img').load(function(){
  //Your code here
 });
});

或者当您预计有大量的图像时:

$.ajaxSuccess(function(){
 $('img:not(.loaded)').addClass('.loaded').bind('load',function(){
  //Your code here
 });
});

如果上述代码中的任何一个都不能正常工作,您可能希望将代码放在回调函数中,在添加后向站点添加html。在上述函数中使用setTimeout也可能有所帮助。
[编辑]
您可以在服务器端代码中生成图像<img src="something" onload="$.imgHello()" />,并在每个$.imgHello()函数调用中递增一些计数器,如果您知道要加载多少个图像。但这不是我推荐给任何人的解决方案。

如果您在页面上有数百张图片,选择仅加载新图片比将新的加载函数绑定到所有图片上要更快。 - naugtur
但是我会针对页面上的所有图片执行相同的功能吗?那不会更加拖慢一切吗?我一直在思考这样一个想法,如果有一种方法让脚本知道页面上有多少张图片(它可以),并且每个图片都可以以某种方式通知脚本完成了,那么脚本就可以根据完成通知执行函数,然后我就可以继续进行了。JavaScript是否具备这样的能力? - dclowd9901
事实上,这就是发生的事情。但仅适用于由单个ajax调用生成的图像。在将图像放在页面之前,您无法将任何内容绑定到事件。您只能做一个小小的hack。请参见编辑。 - naugtur
哎呀,我觉得jQuery .load()方法不是一个事件处理程序,而是一个内容重写器。也许应该改成$('img').one('load', function(){ - Bob Stein

3
循环遍历图像并调用 $('img').complete 的问题在于浏览器 UI 是单线程的,因此您从未给它加载图像的机会,因为它始终在您的循环中。您可以使用 setTimeout 来让浏览器工作。例如:
$.get("whatever",function(html) {
   $('#foo').html(html);
   var images = getTheImages();
   function checkImages() {
      var done = true;
      $.each(images,function() {
         done = done && this.complete;
         return done;
      });
      if(done) {
        fireEvent();
      } else {
        setTimeout(checkImages,100); // wait at least 100 ms and check again
      }
   }
});

我也尝试过这个方法,但它仍然没有在正确的时间触发。也许我的方法有误,但它看起来与你在这里写的非常相似。 - dclowd9901

2
尝试了各种方法后,这个方案终于奏效了,特别感谢Naugtur指点我正确的方向:
var no_of_images = $('img').size();

$.extend(no_of_images);

if(no_of_images > 0) {

    var images = new Array();
    var i = 0;
    $('img').each( function() {
        images[i] = $(this).attr('src');
        i++;
    });

    i = 0;

    $(images).each( function(){
        var imageObj = new Image();
        imageObj.src = images[i];
        $(imageObj).load( function() {
            no_of_images--;
            i++;
            if(no_of_images == 0){
                functionToPerform();
            }
        });
    });
} else {
    functionToPerform();
}

为了简单地解释正在发生的事情,我会使用一个比喻。想象每个图像都是你派对上的客人。灯灭了。你打开灯,然后赶到每个客人那里,给他们一张纸、一支笔和一部手机,同时记录你向多少人分发了材料。他们开始疯狂地画画,然后一旦完成他们的绘画,就会打电话告诉你。对于每个打电话的人,你都会在黑板上划掉一个计数。当最后一个人打电话给你(当你的黑板上没有计数时),你告诉他们挂断电话并叫披萨店。现在是吃披萨的时间了。如果一开始没有客人,你自己打电话给披萨店。
如果你在想“为什么他要为每一个$.load()创建一个Image()对象”,好吧,我试着按照传统的jQuery方式,使用一个直接的jQuery-style选择器而不是image对象。它似乎被完全忽略了(导致页面崩溃)。我尝试使用Image()与.onLoad结合使用,这样做可以避免页面崩溃,但它没有执行我编写它的功能。
这段代码是自包含的,所以我想它可以被复制粘贴到任何图片加载应用程序中。在我看来,它足够小,不会显得突兀。functionToPerform()基本上扮演任何你想要等待执行的函数,直到所有图像都加载完成(在我的情况下,它是整个插件)。拜拜了您嘞。

为什么要调用 $.extend(no_of_images); - Josh Schumacher

1

你有没有想过使用live()来绑定所有现有和未来的img元素?

$('img').live('load', function ()
{
    alert("image loaded: "+this.src);
});

在你的更新后,我能够看到你遇到的问题。 live() 最适合用于选择器,而你正在将其用于 jQuery 封装的元素数组上。像这样使用 live() 与使用 bind() 没有什么区别。你应该尝试像这样做:
if($('.simpleSlide-slide img').size() > 0) {
    var loaded_materials = '.simpleSlide-slide img';
} else {
    var loaded_materials = '.simpleSlide-slide';
}

$(loaded_materials).live('load', function() {
         /* Image processing and loading code */
});

我昨晚尝试了这个,但似乎没有起作用,除非我完全错过了语法,但是对我来说,Live并不那么复杂。虽然我相信它本质上不支持“load”作为事件... - dclowd9901
1
你是机器人!我正在打我的答案 :P 这不公平 ;) - naugtur
@dclowd9901:文档中没有提到不支持的事件。只是说“包含JavaScript事件类型的字符串,例如'click'或'keydown'。”它甚至声称支持自定义事件,所以我相信它可以支持标准事件 :-) - Andy E
@Andy 是的,它支持所有事件,但只在最新版本的jquery中。它曾经只能使用一部分可用事件,并且他们必须迅速修复它,因为人们不断滥用live()函数。我个人更喜欢跟踪自己正在做什么,并尽可能使用bind()。 - naugtur
再试一次。就好像这不是 .live() 的功能使用或其他的原因。通过 Firebug 断点调试,它到达了那一行,然后就挂掉了。它不会继续执行下去。 - dclowd9901

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