Préchargement d'images avec jQuery

689

Je recherche un moyen rapide et facile de précharger des images avec JavaScript. J'utilise jQuery si c'est important.

Je l'ai vu ici ( 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" });
  }
};

Mais ça a l'air un peu exagéré pour ce que je veux!

Je sais qu'il existe des plugins jQuery qui font cela, mais ils semblent tous un peu gros (en taille); J'ai juste besoin d'un moyen rapide, facile et court de précharger des images!


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

Réponses:

969

Rapide et facile:

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

Ou, si vous voulez un plugin jQuery:

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

// Usage:

$(['img1.jpg','img2.jpg','img3.jpg']).preload();
James
la source
25
L'élément image ne doit-il pas être inséré dans le DOM pour garantir que le navigateur le cache?
JoshNaro
8
Je crois $('<img />')que crée juste un élément d'image en mémoire (voir lien ). Il semble que '$('<img src="' + this + '" />')cela créerait réellement l'élément dans une DIV cachée, car il est plus "compliqué". Cependant, je ne pense pas que cela soit nécessaire pour la plupart des navigateurs.
JoshNaro
104
C'est une façon étrange d'écrire un plugin jQuery. Pourquoi ne pas $.preload(['img1.jpg', 'img2.jpg'])?
alex
12
Assurez-vous d'appeler cela à l'intérieur, $(window).load(function(){/*preload here*/});car de cette façon, toutes les images du document sont chargées en premier, il est probable qu'elles soient nécessaires en premier.
Jasper Kennis
12
@RegionalC - Il peut être un peu plus sûr de définir l' loadévénement avant de définir le srcjuste au cas où l'image finirait de se charger avant que l'événement de chargement ne soit défini? $('<img/>').load(function() { /* it's loaded! */ }).attr('src', this);
sparebytes
102

Voici une version modifiée de la première réponse qui charge réellement les images dans DOM et les masque par défaut.

function preload(arrayOfImages) {
    $(arrayOfImages).each(function () {
        $('<img />').attr('src',this).appendTo('body').css('display','none');
    });
}
Dennis Rongo
la source
38
hide()est plus laconique que css('display', 'none').
alex
6
Quel est l'avantage de les insérer dans le DOM?
alex
Je voudrais également savoir l'avantage de les insérer dans le DOM.
jedmao
11
D'après mon expérience, le préchargement d'une image dans le DOM rend le navigateur conscient de son existence et qu'il soit correctement mis en cache. Sinon, l'image n'existe qu'en mémoire, ce qui ne fonctionne que pour les applications d'une seule page.
Dennis Rongo
Dennis Rongo. Ajout d'images au DOM fixe de rechargement aléatoire sur Chrome. Merci!
tmorell
52

Utilisez l' objet Image JavaScript .

Cette fonction vous permet de déclencher un rappel lors du chargement de toutes les images. Cependant, notez qu'il ne déclenchera jamais de rappel si au moins une ressource n'est pas chargée. Cela peut être facilement résolu en implémentant le onerrorrappel et en incrémentant la loadedvaleur ou en gérant l'erreur.

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');
});
Gajus
la source
4
Faites la bonne chose, utilisez l'objet Image JavaScript. avez-vous vu des gens faire la mauvaise chose dans ces réponses?
alex
Code brillant. Ai-je raison de penser que cela déclenchera l' onloadévénement même si l'image est mise en cache? (Parce que img.onloadest déclaré en premier). C'est ce que mes tests ont montré.
Startec
3
@alex potentiellement, oui. Si l'objectif est de précharger (ce qui suggère un ordre de performances), je préférerais voir une option JS brute au lieu d'options dépendantes de jQuery.
Charlie Schliesser
J'ai adoré cette solution mais je viens de découvrir qu'elle ne fonctionne pas dans mon Firefox. Est-ce juste moi ou d'autres ont-ils le même problème?
Gazillion
@Gazillion donne plus de détails. Comment définissez-vous «ne fonctionne pas»? Qu'est-ce que la version FF?
Gajus
35

JP, Après avoir vérifié votre solution, je rencontrais toujours des problèmes dans Firefox où il ne préchargeait pas les images avant de passer au chargement de la page. J'ai découvert cela en en mettant sleep(5)dans mon script côté serveur. J'ai implémenté la solution suivante basée sur la vôtre qui semble résoudre ce problème.

Fondamentalement, j'ai ajouté un rappel à votre plugin de préchargement jQuery, afin qu'il soit appelé une fois que toutes les images sont correctement chargées.

// 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(); }
    });
  });
};

Par intérêt, dans mon contexte, j'utilise ceci comme suit:

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

J'espère que cela aidera quelqu'un qui vient sur cette page de Google (comme moi) à la recherche d'une solution pour précharger les images sur les appels Ajax.

Dave
la source
2
Pour ceux qui ne sont pas intéressés à gâcher le Array.prototype, à la place, vous pouvez faire: checklist = this.lengthEt dans la fonction de checklist--if (checklist == 0) callback();
chargement
3
Tout irait bien, mais l'ajout de nouvelles méthodes ou la suppression de méthodes existantes à un objet que vous ne possédez pas est l'une des pires pratiques de JavaScript.
Rihards
Agréable et rapide, mais ce code ne déclenchera jamais le rappel si l'une des images est cassée ou incapable de se charger.
SammyK
.attr({ src: this }).load(function() {...})devrait être .load(function() {...}).attr({ src: this })sinon vous aurez des problèmes de mise en cache.
Kevin B
25

Ce code jQuery d'une ligne crée (et charge) un élément DOM img sans le montrer:

$('<img src="img/1.jpg"/>');
rkcell
la source
4
@huzzah - Il vaut peut-être mieux utiliser des sprites. Moins de requêtes 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;
  });
};

L'utilisation est assez simple:

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

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

yckart
la source
C'est une très bonne réponse, elle devrait vraiment être la réponse la plus votée à mon humble avis.
sleepycal
1
Quelques mots explicatifs auraient été bien.
robsch
Bonne réponse, mais je suggère d'utiliser picsum.photos au lieu de lorempixel.com
Aleksandar
19

J'ai un petit plugin qui gère cela.

Cela s'appelle waitForImages et il peut gérer des imgéléments ou n'importe quel élément avec une référence à une image dans le CSS, par exemple div { background: url(img.png) }.

Si vous vouliez simplement charger toutes les images, y compris celles référencées dans le CSS, voici comment vous le feriez :)

$('body').waitForImages({
    waitForAll: true,
    finished: function() {
       // All images have loaded.
    }  
});
alex
la source
Ce plugin provoque-t-il le chargement d'images qui ne sont pas apparues sur la page, ou attache-t-il uniquement des événements aux images qui allaient déjà être chargées?
Dave Cameron,
@DaveCameron Il ne respecte pas si les images sont visibles ou non. Vous pouvez facilement le bifurquer et apporter ce changement - il suffit d'ajouter :visibleau sélecteur personnalisé.
alex
Ce plugin semble très intéressant. Cependant, la documentation jquery souligne que l' loadévénement, lorsqu'il est appliqué aux images: "ne fonctionne pas de manière cohérente ni fiable entre les navigateurs". Comment le plugin a-t-il réussi à contourner cela?
EleventyOne
@EleventyOne Vérifiez la source en ce qui concerne le sélecteur personnalisé.
alex
@alex Comment ce plugin charge-t-il les images définies dans le CSS sur: survol d'un élément? Cela ne fonctionne pas pour moi
Philipp Hofmann
13

vous pouvez charger des images dans votre html quelque part en utilisant la display:none;règle css , puis les afficher quand vous le souhaitez avec js ou jquery

n'utilisez pas les fonctions js ou jquery pour précharger n'est qu'une règle css contre plusieurs lignes de js à exécuter

exemple: 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();

Le préchargement d'images par jquery / javascript n'est pas bon car les images prennent quelques millisecondes à charger dans la page + vous avez des millisecondes pour que le script soit analysé et exécuté, surtout si ce sont de grandes images, il est donc préférable de les masquer en hml pour les performances, car l'image est vraiment préchargée sans être visible du tout, jusqu'à ce que vous le montriez!

c'est moi
la source
2
Plus d' informations sur cette approche peut être trouvée ici: perishablepress.com/...
gdelfino
3
Mais vous devez être conscient que cette technique présente un inconvénient majeur: votre page ne sera pas complètement chargée tant que toutes les images ne seront pas chargées. Selon le nombre d'images à précharger et leur taille, cela peut prendre un certain temps. Pire encore, si la balise <img> ne spécifie pas de hauteur et de largeur, certains navigateurs peuvent attendre que l'image soit récupérée avant de restituer le reste de la page.
@Alex, vous devez de toute façon charger l'img, vous êtes libre de choisir de les charger avec du html et d'éviter tout type d'images clignotantes et à moitié chargées, ou si vous voulez obtenir plus de vitesse pour une solution non stable
itsme
aussi je pense vraiment que la création de balises html avec javascript est illisible du tout juste mes 2 cents
itsme
1
J'avais besoin d'une solution très rapide pour un projet très complexe et c'était tout.
Germstorm
13

ce plugin jquery imageLoader ne fait que 1,39 ko

usage:

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

il y a aussi d'autres options comme si vous voulez charger les images de façon synchrone ou asychronique et un événement complet pour chaque image individuelle.

alzclarke
la source
@AamirAfridi Pourquoi ça?
Ian
1
@Ian car le document est déjà prêt au moment où le rappel est déclenché. $ (document) .ready est utilisé pour s'assurer que votre DOM est chargé. En ce qui concerne le rappel, cela signifie que toutes les images sont chargées, ce qui signifie que votre DOM est chargé, donc pas besoin de document.ready dans un rappel.
Aamir Afridi
1
@AamirAfridi Il n'y a aucune garantie que le document est prêt dans le rappel ... d'où déduisez-vous cela? Il n'y a rien sur la page du plugin qui indique que le allcompleterappel est exécuté une fois que le DOM est prêt. Il y a de fortes chances que le chargement des images se termine une fois que le DOM est prêt, mais il n'y a aucune raison de supposer. Je ne sais pas pourquoi vous pensez que les rappels sont magiques et sont exécutés après que le DOM soit prêt ... pouvez-vous expliquer d'où vous obtenez cela? Ce n'est pas parce que les images sont chargées que le DOM est prêt
Ian
@AamirAfridi Et la documentation du plugin dit même: NB to use this as an image preloader simply put your $(document).ready(function(){}); into your 'allcomplete' event : > simples!... il recommande directement ce que montre cette réponse.
Ian
5
@AamirAfridi Cela n'a aucun sens pour cette situation. Le plugin est un pré- chargeur. Vous souhaitez l'exécuter dès que possible. Et il y a beaucoup de choses qui ne dépendent pas du DOM que vous souhaitez déclencher dès que possible, puis lorsque le DOM est prêt, faites quelque chose.
Ian
9

Un moyen rapide et sans plug-in de précharger des images dans jQuery et d'obtenir une fonction de rappel consiste à créer plusieurs imgbalises à la fois et à compter les réponses, par exemple

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. */
});
    

Notez que si vous souhaitez prendre en charge IE7, vous devrez utiliser cette version légèrement moins jolie (qui fonctionne également dans d'autres navigateurs):

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

Merci pour cela! Je voudrais ajouter un petit riff à la réponse du JP - je ne sais pas si cela va aider quelqu'un, mais de cette façon vous n'avez pas à créer un tableau d'images, et vous pouvez précharger toutes vos grandes images si vous nommez vos pouces correctement. C'est pratique car j'ai quelqu'un qui écrit toutes les pages en html, et cela leur assure une étape de moins - éliminant le besoin de créer le tableau d'images, et une autre étape où les choses pourraient être gâchées.

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

Fondamentalement, pour chaque image de la page, elle saisit le src de chaque image, si elle correspond à certains critères (c'est un pouce ou une image de page d'accueil), elle change le nom (une chaîne de base remplace dans l'image src), puis charge les images .

Dans mon cas, la page était pleine d'images de pouce toutes nommées quelque chose comme image_th.jpg, et toutes les grandes images correspondantes sont nommées image_lg.jpg. Le pouce vers grand remplace simplement le _th.jpg par _lg.jpg, puis précharge toutes les grandes images.

J'espère que cela aide quelqu'un.

dgig
la source
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*/
        }
    )
Alex
la source
7
Bienvenue dans Stack Overflow! Plutôt que de publier uniquement un bloc de code, veuillez expliquer pourquoi ce code résout le problème posé. Sans explication, ce n'est pas une réponse.
Martijn Pieters
3

J'utilise le code suivant:

$("#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";
Cristan
la source
Liez d'abord à l'événement de chargement, puis définissez le src.
Kevin B
1

J'utiliserais un fichier Manifest pour dire aux navigateurs Web (modernes) de charger également toutes les images pertinentes et de les mettre en cache. Utilisez Grunt et grunt-manifest pour le faire automatiquement et ne vous inquiétez plus jamais des scripts de préchargement, des invalidateurs de cache, du CDN, etc.

https://github.com/gunta/grunt-manifest

Christian Landgren
la source
0

Cela fonctionne pour moi même dans IE9:

$('<img src="' + imgURL + '"/>').on('load', function(){ doOnLoadStuff(); });
Zymotik
la source
1
Cela échouera éventuellement en raison de la mise en cache, toujours lier l'événement de chargement avant de définir l'attribut src.
Kevin B
0

Je voulais le faire avec une superposition personnalisée de l'API Google Maps. Leur exemple de code utilise simplement JS pour insérer des éléments IMG et la zone d'espace réservé pour l'image s'affiche jusqu'à ce que l'image soit chargée. J'ai trouvé ici une réponse qui a fonctionné pour moi: https://stackoverflow.com/a/10863680/2095698 .

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

Cela précharge une image comme suggéré précédemment, puis utilise le gestionnaire pour ajouter l'objet img après le chargement de l'URL img. La documentation de jQuery prévient que les images mises en cache ne fonctionnent pas bien avec ce code d'événement / gestionnaire, mais cela fonctionne pour moi dans FireFox et Chrome, et je n'ai pas à me soucier d'IE.

bwinchester
la source
Cela ne fonctionnera pas bien avec les images mises en cache comme vous l'avez découvert. la solution consiste à lier d'abord l'événement de chargement, puis à définir l'attribut src.
Kevin B
-1
function preload(imgs) {
    $(imgs).each(function(index, value) {
        $('<img />').attr('src', value).appendTo('body').css('display', 'none');
    });
}

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

juste pour le signaler :)

Whisher
la source
La portée thisà l'intérieur du rappel transmis à $.eachest affectée à la valeur en cours d'itération.
Oybek
? $ (['img1.jpg', 'img2.jpg', 'img3.jpg']). each (fonction (index, valeur) {console.log (valeur); // img1.jpg console.log (this) ; // Chaîne {0 = "i", 1 = "m", 2 = "g", plus ...} $ ('<img />'). Attr ('src', this) .appendTo (' body '). css (' display ',' none ');});
Whisher
Hm. Je suppose que vous êtes ici. Par exemple, $("div").each(function(i, el){ console.log(el == this);});génère tous les trues; L'itération sur le tableau semble se comporter différemment.
Oybek
-2

5 lignes en coffeescript

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
Guy Morita
la source
2
Pouvez-vous développer le fonctionnement de la solution pour résoudre la question dans le PO? Et éventuellement commenter votre code pour que les autres le comprennent plus facilement?
Ro Yo Mi
-4

Pour ceux qui connaissent un peu le script d'action, vous pouvez vérifier le lecteur flash, avec un minimum d'effort, et créer un préchargeur flash, que vous pouvez également exporter vers html5 / Javascript / Jquery. Pour l'utiliser si le lecteur flash n'est pas détecté, consultez des exemples sur la façon de le faire avec le rôle YouTube sur le lecteur html5 :) Et créez le vôtre. Je n'ai pas les détails, car je n'ai pas encore commencé, si je n'ai pas oublié, je le posterai plus tard et j'essaierai un code Jquery standard pour le mien.

Peter Gruppelaar
la source
Ce n'est pas une solution. Le navigateur ne prend pas en charge Flash Player par défaut. Cela peut être facilement réalisé avec JavaScript qui est la langue native du navigateur.
Usman Ahmed
Je me souviens que les ennemis de Flash de 2012 ... ce n'était pas normal ... partout où je suis allé, j'ai été attaqué quand j'ai même utilisé le mot Flash.
Peter Gruppelaar