用jQuery预加载图像


689

我正在寻找一种快速简单的方法来用JavaScript预加载图像。如果重要的话,我正在使用jQuery。

我在这里(http://nettuts.com ...)看到了:

function complexLoad(config, fileNames) {
  for (var x = 0; x < fileNames.length; x++) {
    $("<img>").attr({
      id: fileNames[x],
      src: config.imgDir + fileNames[x] + config.imgFormat,
      title: "The " + fileNames[x] + " nebula"
    }).appendTo("#" + config.imgContainer).css({ display: "none" });
  }
};

但是,对于我想要的东西来说似乎有点过高了!

我知道那里有执行此操作的jQuery插件,但是它们看上去都很大(大小);我只需要一种快速,轻松和简短的预加载图像方式!


10
$.each(arguments,function(){(new Image).src=this});
David Hellsing

Answers:


969

快速简单的:

function preload(arrayOfImages) {
    $(arrayOfImages).each(function(){
        $('<img/>')[0].src = this;
        // Alternatively you could use:
        // (new Image()).src = this;
    });
}

// Usage:

preload([
    'img/imageName.jpg',
    'img/anotherOne.jpg',
    'img/blahblahblah.jpg'
]);

或者,如果您想要一个jQuery插件:

$.fn.preload = function() {
    this.each(function(){
        $('<img/>')[0].src = this;
    });
}

// Usage:

$(['img1.jpg','img2.jpg','img3.jpg']).preload();

25
不需要将图像元素插入DOM来确保浏览器对其进行缓存吗?
JoshNaro

8
我相信$('<img />')只是在内存中创建一个图像元素(请参阅链接)。看起来'$('<img src="' + this + '" />')实际上是在隐藏的DIV中创建该元素,因为它更加“复杂”。但是,我认为大多数浏览器都不需要这样做。
JoshNaro 2011年

104
那是编写jQuery插件的怪异方法。为什么不$.preload(['img1.jpg', 'img2.jpg'])呢?
alex 2012年

12
确保在内部调用此方法,$(window).load(function(){/*preload here*/});因为那样会首先加载文档中的所有图像,很可能首先需要使用它们。
贾斯珀·肯尼斯

12
@RegionalC-设置load事件之前设置事件可能更安全一些,以防万一在设置src加载事件之前完成图像加载?$('<img/>').load(function() { /* it's loaded! */ }).attr('src', this);
13年

102

这是第一个响应的经过调整的版本,该响应实际上将图像加载到DOM中并默认将其隐藏。

function preload(arrayOfImages) {
    $(arrayOfImages).each(function () {
        $('<img />').attr('src',this).appendTo('body').css('display','none');
    });
}

38
hide()css('display', 'none')。更简洁。
Alex

6
将它们插入DOM有什么好处?
亚历克斯

我也想知道将它们插入DOM的好处。
jedmao

11
根据我的经验,将图像预加载到DOM中会使浏览器意识到其存在并对其进行适当的缓存。否则,该图像仅存在于内存中,该内存仅适用于单页应用程序。
丹尼斯·荣戈

丹尼斯·朗戈。将图片添加到DOM可以解决Chrome上随机重新加载的问题。谢谢!
tmorell

52

使用JavaScript Image对象

此功能允许您在加载所有图片时触发回调。但是,请注意,如果没有加载至少一个资源,它将永远不会触发回调。这可以通过实现onerror回调和递增loaded值或处理错误来轻松解决。

var preloadPictures = function(pictureUrls, callback) {
    var i,
        j,
        loaded = 0;

    for (i = 0, j = pictureUrls.length; i < j; i++) {
        (function (img, src) {
            img.onload = function () {                               
                if (++loaded == pictureUrls.length && callback) {
                    callback();
                }
            };

            // Use the following callback methods to debug
            // in case of an unexpected behavior.
            img.onerror = function () {};
            img.onabort = function () {};

            img.src = src;
        } (new Image(), pictureUrls[i]));
    }
};

preloadPictures(['http://foo/picture.bar', 'http://foo/picture.bar', 'http://foo/picture.bar', 'http://foo/picture.bar'], function(){
    console.log('a');
});

preloadPictures(['http://foo/picture.bar', 'http://foo/picture.bar', 'http://foo/picture.bar', 'http://foo/picture.bar'], function(){
    console.log('b');
});

4
做正确的事,使用JavaScript Image对象。您在这些答案中发现有人做错事了吗?
亚历克斯

出色的代码。我是否认为onload即使缓存了图像也会触发事件,是否正确?(因为img.onload先声明)。这是我的测试所显示的。
Startec

3
@alex可能,是的。如果目标是预加载(表示性能顺序),那么我希望看到一个原始的JS选项,而不是jQuery依赖的选项。
Charlie Schliesser 2014年

我喜欢这个解决方案,但是我发现它不能在Firefox中使用。是我还是其他人有同样的问题?
Gazillion

@Gazillion提供更多详细信息。您如何定义“无效”?FF版本是什么?
Gajus 2014年

35

JP,检查完您的解决方案后,我在Firefox中仍然遇到问题,即在继续加载页面之前无法预加载图像。我通过sleep(5)在服务器端脚本中放置一些脚本来发现这一点。我根据您的情况实施了以下解决方案,似乎可以解决此问题。

基本上,我向您的jQuery preload插件添加了一个回调,以便在正确加载所有图像之后调用该回调。

// Helper function, used below.
// Usage: ['img1.jpg','img2.jpg'].remove('img1.jpg');
Array.prototype.remove = function(element) {
  for (var i = 0; i < this.length; i++) {
    if (this[i] == element) { this.splice(i,1); }
  }
};

// Usage: $(['img1.jpg','img2.jpg']).preloadImages(function(){ ... });
// Callback function gets called after all images are preloaded
$.fn.preloadImages = function(callback) {
  checklist = this.toArray();
  this.each(function() {
    $('<img>').attr({ src: this }).load(function() {
      checklist.remove($(this).attr('src'));
      if (checklist.length == 0) { callback(); }
    });
  });
};

在我的上下文中,出于兴趣,我按如下方式使用它:

$.post('/submit_stuff', { id: 123 }, function(response) {
  $([response.imgsrc1, response.imgsrc2]).preloadImages(function(){
    // Update page with response data
  });
});

希望这可以帮助从Google转到此页面的人(像我一样)寻找一种解决方案,以在Ajax调用中预加载图像。


2
对于不希望弄乱Array.prototype的用户,可以执行以下操作:checklist = this.length在onload函数中:checklist--然后:if (checklist == 0) callback();
MiniGod 2012年

3
一切都很好,但是向您不拥有的对象添加新方法或删除现有方法是JavaScript中最差的做法之一。
Rihards 2012年

不错,快捷,但是如果其中一张图片损坏或无法加载,则此代码将永远不会触发回调。
SammyK

.attr({ src: this }).load(function() {...}).load(function() {...}).attr({ src: this })否则你会有缓存问题。
凯文B

25

这段jQuery代码创建(并加载)一个DOM元素img而不显示它:

$('<img src="img/1.jpg"/>');

4
@huzzah-您最好只使用精灵。更少的http请求。:)
Alex K

22
$.fn.preload = function (callback) {
  var length = this.length;
  var iterator = 0;

  return this.each(function () {
    var self = this;
    var tmp = new Image();

    if (callback) tmp.onload = function () {
      callback.call(self, 100 * ++iterator / length, iterator === length);
    };

    tmp.src = this.src;
  });
};

用法很简单:

$('img').preload(function(perc, done) {
  console.log(this, perc, done);
});

http://jsfiddle.net/yckart/ACbTK/


这是一个非常好的答案,它确实应该是恕我直言的最高投票答案。
sleepycal 2014年

1
一些解释性的话会很好。
罗布施(Robsch)'17

好的答案,但是我建议使用picsum.photos代替lorempixel.com
Aleksandar

19

我有一个处理这个问题的小插件。

它称为waitForImages,它可以处理img元素或引用CSS中图像的任何元素,例如div { background: url(img.png) }

如果您只是想加载所有图像,包括CSS中引用的图像,请按以下步骤操作:)

$('body').waitForImages({
    waitForAll: true,
    finished: function() {
       // All images have loaded.
    }  
});

这个插件会导致尚未显示在页面上的图像被加载,还是仅将事件附加到已经要加载的图像上?
戴夫·卡梅伦

@DaveCameron它不考虑图像是否可见。您可以轻松地进行分叉并进行更改-只需添加:visible到自定义选择器即可。
alex 2012年

这个插件看起来非常有趣。但是,jquery文档强调了该load事件在应用于图像时:“无法始终如一地运行,也不可靠地跨浏览器运行”。插件如何解决这个问题?
EleventyOne 2013年

@EleventyOne检查有关自定义选择器的源。
Alex

@alex此插件如何加载元素:hover上css中设置的图像?它对我不起作用
Philipp Hofmann 2014年

13

您可以使用css display:none;规则在html的某处加载图片,然后在需要时使用js或jquery显示它们

不要使用js或jquery函数来预加载只是一个CSS规则,而要执行的js行很多

范例:HTML

<img src="someimg.png" class="hide" alt=""/>

CSS:

.hide{
display:none;
}

jQuery的:

//if want to show img 
$('.hide').show();
//if want to hide
$('.hide').hide();

通过jquery / javascript预加载图片效果不好,原因是图片需要花费几毫秒的时间才能加载到页面中+您有几毫秒的时间来解析和执行脚本,特别是如果它们是大图片,那么将它们隐藏在hml中也会带来更好的性能,导致图像真正被预加载而根本看不到蜂鸣声,直到您显示为止!


2
这种方法的更多信息可以在这里找到:perishablepress.com/...
gdelfino

3
但是您需要意识到这种技术有一个主要的缺点:在加载所有图像之前,不会完全加载页面。根据要预加载的图像数量及其大小,这可能需要一些时间。更糟糕的是,如果<img>标签未指定高度和宽度,则某些浏览器可能会等到获取图像后再呈现页面的其余部分。

@Alex无论如何都需要加载img,您可以自由选择是否用html加载它们,并避免任何形式的闪烁和一半加载的图像,或者如果您想获得更快的速度来寻求一个不稳定的解决方案
itsme

我也真的觉得用javascript创建html标签根本不可读,仅占我的2美分
itsme

1
我需要一个非常复杂的项目的快速解决方案,就是这样。
Germstorm 2013年

13

这个jquery imageLoader插件只有1.39kb

用法:

$({}).imageLoader({
    images: [src1,src2,src3...],
    allcomplete:function(e,ui){
        //images are ready here
        //your code - site.fadeIn() or something like that
    }
});

还有其他选项,例如您是否要同步或异步加载图像,以及每个图像的完整事件。


@AamirAfridi为什么?
2013年

1
@Ian,因为在触发回调时文档已经准备就绪。$(document).ready用于确保已加载DOM。当涉及到回调时,这意味着所有图像都已加载,这意味着已加载yoru DOM,因此在回调内部无需document.ready。
Aamir Afridi

1
@AamirAfridi无法保证回调中的文档准备就绪...您从哪里推断出该文档?插件页面上没有任何内容表明allcomplete在DOM准备就绪后执行了回调。在DOM准备好后,图像很有可能完成加载,但是没有理由假设。我不知道为什么您认为回调是神奇的,并且在DOM准备就绪后执行回调...您能解释一下从哪里获得回调吗?仅仅因为图像已加载并不意味着DOM已经准备就绪
Ian

@AamirAfridi插件的文档甚至说:NB to use this as an image preloader simply put your $(document).ready(function(){}); into your 'allcomplete' event : > simples!...它直接建议此答案显示的内容。
2013年

5
@AamirAfridi对于这种情况,这没有任何意义。该插件是一个加载器。您想尽快执行它。并且有很多事情与您希望尽快启动的DOM不相关,然后在DOM准备就绪时执行一些操作。
2013年

9

快速,无需插件的方式将图像预加载到jQuery中并获得回调函数的方法是img一次创建多个标签并计算响应,例如

function preload(files, cb) {
    var len = files.length;
    $(files.map(function(f) {
        return '<img src="'+f+'" />';
    }).join('')).load(function () {
        if(--len===0) {
            cb();
        }
    });
}

preload(["one.jpg", "two.png", "three.png"], function() {
    /* Code here is called once all files are loaded. */
});
    

请注意,如果您想支持IE7,则需要使用这个不太漂亮的版本(在其他浏览器中也可以使用):

function preload(files, cb) {
    var len = files.length;
    $($.map(files, function(f) {
        return '<img src="'+f+'" />';
    }).join('')).load(function () {
        if(--len===0) {
            cb();
        }
    });
}

7

谢谢你!我希望在JP的答案上加上一点即兴之处-我不知道这是否对任何人都有帮助,但是这样您就不必创建图像阵列了,并且您可以预加载所有大图像正确命名您的拇指。这很方便,因为我有一个用html编写所有页面的人,它确保了他们要做的少一步-消除了创建图像数组的需要,而另一步可能使事情搞砸了。

$("img").each(function(){
    var imgsrc = $(this).attr('src');
    if(imgsrc.match('_th.jpg') || imgsrc.match('_home.jpg')){
      imgsrc = thumbToLarge(imgsrc);
      (new Image()).src = imgsrc;   
    }
});

基本上,页面上的每个图像都会抓取每个图像的src,如果它符合特定条件(是拇指图像或主页图像),它将更改名称(图像src中的基本字符串替换),然后加载图像。

在我的情况下,页面上充斥着拇指图像,所有图像均命名为image_th.jpg,所有相应的大图像均命名为image_lg.jpg。大拇指将_th.jpg替换为_lg.jpg,然后预加载所有大图像。

希望这对某人有帮助。


3
    jQuery.preloadImage=function(src,onSuccess,onError)
    {
        var img = new Image()
        img.src=src;
        var error=false;
        img.onerror=function(){
            error=true;
            if(onError)onError.call(img);
        }
        if(error==false)    
        setTimeout(function(){
            if(img.height>0&&img.width>0){ 
                if(onSuccess)onSuccess.call(img);
                return img;
            }   else {
                setTimeout(arguments.callee,5);
            }   
        },0);
        return img; 
    }

    jQuery.preloadImages=function(arrayOfImages){
        jQuery.each(arrayOfImages,function(){
            jQuery.preloadImage(this);
        })
    }
 // example   
    jQuery.preloadImage(
        'img/someimage.jpg',
        function(){
            /*complete
            this.width!=0 == true
            */
        },
        function(){
            /*error*/
        }
    )

7
欢迎使用Stack Overflow!而不是仅发布代码块,请解释为什么此代码解决了所提出的问题。没有解释,这不是答案。
马丁·彼得斯

3

我使用以下代码:

$("#myImage").attr("src","img/spinner.gif");

var img = new Image();
$(img).load(function() {
    $("#myImage").attr("src",img.src);
});
img.src = "http://example.com/imageToPreload.jpg";

首先绑定到load事件,然后设置src。
凯文B


0

即使在IE9中,这也对我有效:

$('<img src="' + imgURL + '"/>').on('load', function(){ doOnLoadStuff(); });

1
这最终将由于缓存而失败,请始终在设置src属性之前绑定load事件。
凯文B

0

我想通过Google Maps API自定义叠加层实现此目的。他们的示例代码仅使用JS插入IMG元素,并显示图像占位符框,直到加载图像为止。我在这里找到了适合我的答案:https : //stackoverflow.com/a/10863680/2095698

$('<img src="'+ imgPaht +'">').load(function() {
$(this).width(some).height(some).appendTo('#some_target');
});

如前所述,这将预加载图像,然后在加载img URL后使用处理程序附加img对象。jQuery的文档警告说,缓存的图像在此事件/处理程序代码中无法很好地工作,但是它在FireFox和Chrome中对我有效,并且我不必担心IE。


如您所知,这不适用于缓存的图像。解决方法是先绑定load事件,然后设置src属性。
凯文B

-1
function preload(imgs) {
    $(imgs).each(function(index, value) {
        $('<img />').attr('src', value).appendTo('body').css('display', 'none');
    });
}

.attr('src',value).attr('src',this)

只是指出来:)


this传递给回调函数的作用域$.each被分配给要迭代的值。
奥伊贝克(Oybek)

?$(['img1.jpg','img2.jpg','img3.jpg'])。each(function(index,value){console.log(value); // img1.jpg console.log(this) ; // String {0 =“ i”,1 =“ m”,2 =“ g”,更多...} $('<img />')。attr('src',this).appendTo(' body')。css('display','none');});
Whisher

嗯 我想你就在这里。例如$("div").each(function(i, el){ console.log(el == this);});生成all true;遍历数组的行为似乎有所不同。
奥伊贝克(Oybek),

-2

Coffeescript中的5行

array = ['/img/movie1.png','/img/movie2.png','/img/movie3.png']

$(document).ready ->
  for index, image of array
    img[index] = new Image()
    img[index].src = image

2
您能否扩展解决方案如何解决OP中的问题?并可能注释您的代码,以便其他人可以更轻松地理解它?
Ro Yo Mi

-4

对于那些只了解一点动作脚本的人,您可以轻松地检查Flash Player,并制作Flash预加载器,也可以将其导出到html5 / Javascript / Jquery。要在未检测到Flash Player的情况下使用,请查看有关如何通过youtube角色回到html5 player进行操作的示例:)然后创建您自己的Flash Player。我没有详细信息,因为我还没有开始,如果我没有忘记,我将在稍后发布它,并将尝试一些标准的Jquery代码来进行挖掘。


这不是解决方案。浏览器默认情况下不支持Flash Player。可以使用本机浏览器语言JavaScript轻松实现。
Usman Ahmed

我记得2012年的Flash仇恨者……那是不正常的……到处走动,我什至都用Flash来攻击我。
彼得·格鲁佩拉尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.