Préchargement d'images avec JavaScript


263

La fonction que j'ai écrite ci-dessous est-elle suffisante pour précharger les images dans la plupart, sinon la totalité, des navigateurs couramment utilisés aujourd'hui?

function preloadImage(url)
{
    var img=new Image();
    img.src=url;
}

J'ai un tableau d'URL d'images que je boucle et appelle la preloadImagefonction pour chaque URL.


19
Notez que certains (tous?) Les navigateurs publieront l'image après quelques secondes si vous ne l'avez pas utilisée. Pour éviter cela, conservez une référence à l' imgobjet, par exemple dans un tableau dans la portée parent.
Tamlyn

3
Qu'entendez-vous par «libérer l'image»? S'il a été mis en cache par le navigateur, il y restera, non?
Francisc

56
Un peu plus court:(new Image()).src = url;
mrzmyr

20
notez que cela ne fonctionnera pas lorsque chrome devtool est ouvert et que 'disable chache' est activé dans le panneau réseau
Wayou

6
Encore plus court:new Image().src = url;
Daniel X Moore

Réponses:


163

Oui. Cela devrait fonctionner sur tous les principaux navigateurs.


48
Note du modérateur: Veuillez cesser de signaler ceci comme "pas une réponse". Il s'agit en fait d'une réponse directe à la question qui a été posée. Si vous pensez que la réponse est fausse ou insuffisamment étayée par des preuves, alors dévalorisez -la.
Cody Gray

43

Essayez ceci, je pense que c'est mieux.

var images = [];
function preload() {
    for (var i = 0; i < arguments.length; i++) {
        images[i] = new Image();
        images[i].src = preload.arguments[i];
    }
}

//-- usage --//
preload(
    "http://domain.tld/gallery/image-001.jpg",
    "http://domain.tld/gallery/image-002.jpg",
    "http://domain.tld/gallery/image-003.jpg"
)

Source: http://perishablepress.com/3-ways-preload-images-css-javascript-ajax/


3
il n'y a aucun onloadgestionnaire pour aucune des images
Benny

12
Pourriez-vous expliquer pourquoi c'est mieux?
JJJ

2
@BeNice Je pense que vous vous méprenez, le chargement d'images est asynchrone, donc vous devez gérer l' onloadétat ou vous instanciez simplement des images en mémoire.
Benny

3
Si vous utilisez cette solution, n'oubliez pas l'instruction var pour la variable i. Sinon, il sera défini globalement, ce qui peut entraîner des erreurs très difficiles à résoudre (sauf si vous savez que vous avez oublié l'instruction var.for (var i = 0.....
Mohammer

2
@Mohammer merci pour cela, je viens de copier le code du lien que j'ai fourni. Je vais modifier le code maintenant pour ajouter levar
clintgh

26

Dans mon cas, il était utile d'ajouter un rappel à votre fonction pour l' onloadévénement:

function preloadImage(url, callback)
{
    var img=new Image();
    img.src=url;
    img.onload = callback;
}

Et puis enveloppez-le pour le cas d'un tableau d'URL vers des images à précharger avec rappel sur tout est fait: https://jsfiddle.net/4r0Luoy7/

function preloadImages(urls, allImagesLoadedCallback){
    var loadedCounter = 0;
  var toBeLoadedNumber = urls.length;
  urls.forEach(function(url){
    preloadImage(url, function(){
        loadedCounter++;
            console.log('Number of loaded images: ' + loadedCounter);
      if(loadedCounter == toBeLoadedNumber){
        allImagesLoadedCallback();
      }
    });
  });
  function preloadImage(url, anImageLoadedCallback){
      var img = new Image();
      img.onload = anImageLoadedCallback;
      img.src = url;
  }
}

// Let's call it:
preloadImages([
    '//upload.wikimedia.org/wikipedia/commons/d/da/Internet2.jpg',
  '//www.csee.umbc.edu/wp-content/uploads/2011/08/www.jpg'
], function(){
    console.log('All images were loaded');
});

19

Alternative à CSS2: http://www.thecssninja.com/css/even-better-image-preloading-with-css2

body:after {
  content: url(img01.jpg) url(img02.jpg) url(img03.jpg);
  display: none; 
}

Alternative à CSS3: https://perishablepress.com/preload-images-css3/ (barrage H / T Linh)

.preload-images {
  display: none; 
  width: 0;
  height: 0;
  background: url(img01.jpg),
              url(img02.jpg),
              url(img03.jpg);
}

REMARQUE: les images dans un conteneur avec display:nonepeuvent ne pas être préchargées. Peut-être que la visibilité: caché fonctionnera mieux mais je n'ai pas testé cela. Merci à Marco Del Valle de l'avoir signalé


Merci mplungjan. Bien que cela ne m'aide pas dans ce cas particulier, c'est bon à savoir.
Francisc

5
Ai-je raison de dire que cela ralentira le chargement de la page, car ces images doivent être téléchargées avant que la page puisse lancer l'événement de chargement?
jakubiszon

3
Je ne pense pas que cela fonctionnera sur tous les navigateurs. Je sais que sur Chrome, les images ne sont chargées que lorsqu'elles sont visibles. Devra supprimer l'affichage: aucun; et essayez plutôt de les positionner de façon à ce qu’elles ne soient pas visibles. Peut ensuite se cacher avec JS après que tout a été chargé si nécessaire.
Bullyen

3
Les images d'arrière-plan d'un élément avec display: nonene seront pas préchargées.
Marquizzo

1
Cette méthode a fonctionné pour moi seulement, je (1) n'ai pas utilisé display: none, (2) n'a pas utilisé le width/height: 0, (3) put no-repeatet une position telle que l'image serait à l'extérieur ( -<width>px -<height>pxvous y amènera, donc si vos images sont toutes à 100px en hauteur ou moins, vous pouvez utiliser 0 -100px, en d'autres termes url(...) no-repeat 0 -100px.
Alexis Wilke

11

Je vous recommande d'utiliser un try / catch pour éviter certains problèmes possibles:

OOP:

    var preloadImage = function (url) {
        try {
            var _img = new Image();
            _img.src = url;
        } catch (e) { }
    }

La norme:

    function preloadImage (url) {
        try {
            var _img = new Image();
            _img.src = url;
        } catch (e) { }
    }

De plus, même si j'aime DOM, les vieux navigateurs stupides peuvent avoir des problèmes avec vous en utilisant DOM, alors évitez-le complètement à mon humble avis contrairement à la contribution de freedev. Image () a un meilleur support dans les anciens navigateurs de corbeille.


7
Je ne pense pas que c'est ainsi que vous détectez les erreurs lors du chargement de l'image en javascript - il existe quelque chose comme _img.onerror qui peut (devrait?) Être utilisé.
Greg0ry

3
Quels sont les "problèmes possibles"?
Kissaki

Des problèmes tels que le support de la commande. Consultez le site "puis-je utiliser" pour voir si un navigateur que vous devez prendre en charge prend en charge une commande javascript.
Dave

10

Cette approche est un peu plus élaborée. Ici, vous stockez toutes les images préchargées dans un conteneur, peut être une div. Et après, vous pourriez montrer les images ou les déplacer dans le DOM à la bonne position.

function preloadImg(containerId, imgUrl, imageId) {
    var i = document.createElement('img'); // or new Image()
    i.id = imageId;
    i.onload = function() {
         var container = document.getElementById(containerId);
         container.appendChild(this);
    };
    i.src = imgUrl;
}

Essayez-le ici , j'ai également ajouté quelques commentaires


7
const preloadImage = src => 
  new Promise(r => {
    const image = new Image()
    image.onload = r
    image.onerror = r
    image.src = src
  })


// Preload an image
await preloadImage('https://picsum.photos/100/100')

// Preload a bunch of images in parallel 
await Promise.all(images.map(x => preloadImage(x.src)))

5

Oui, cela fonctionnera, mais les navigateurs limiteront (entre 4 et 8) les appels réels et donc ne mettront pas en cache / préchargeront toutes les images souhaitées.

Une meilleure façon de le faire est d'appeler onload avant d'utiliser l'image comme ceci:

function (imageUrls, index) {  
    var img = new Image();

    img.onload = function () {
        console.log('isCached: ' + isCached(imageUrls[index]));
        *DoSomething..*

    img.src = imageUrls[index]
}

function isCached(imgUrl) {
    var img = new Image();
    img.src = imgUrl;
    return img.complete || (img .width + img .height) > 0;
}

Pouvez-vous s'il vous plaît lier une référence sur cette limite de navigateurs?
nulll

Il est difficile de trouver une référence (au moins je n'en ai jamais trouvé), mais j'ai utilisé mon propre projet pour le tester tout en examinant de près ce qui a été chargé à partir du cache et des appels de serveur à l'aide de l'onglet réseau de Chrome dans les outils de développement Chrome (f12). J'ai trouvé le même comportement dans Firefox et en utilisant mobile.
Robin

1
-1 parce que j'ai soigneusement testé l'allégation dans la première phrase ici et l'ai trouvée fausse, au moins dans les navigateurs modernes. Voir stackoverflow.com/a/56012084/1709587. Oui, le nombre maximal de requêtes HTTP parallèles que le navigateur est prêt à envoyer est limité, mais il finit par demander à chaque URL que vous appelez preloadImage, même si vous le faites de la manière indiquée dans la question.
Mark Amery

5

Voici mon approche:

var preloadImages = function (srcs, imgs, callback) {
    var img;
    var remaining = srcs.length;
    for (var i = 0; i < srcs.length; i++) {
        img = new Image;
        img.onload = function () {
            --remaining;
            if (remaining <= 0) {
                callback();
            }
        };
        img.src = srcs[i];
        imgs.push(img);
    }
};

4

Solution pour les navigateurs compatibles ECMAScript 2017

Remarque: cela fonctionnera également si vous utilisez un transpilateur comme Babel.

'use strict';

function imageLoaded(src, alt = '') {
    return new Promise(function(resolve) {
        const image = document.createElement('img');

        image.setAttribute('alt', alt);
        image.setAttribute('src', src);

        image.addEventListener('load', function() {
            resolve(image);
        });
    });
}

async function runExample() {
    console.log("Fetching my cat's image...");

    const myCat = await imageLoaded('https://placekitten.com/500');

    console.log("My cat's image is ready! Now is the time to load my dog's image...");

    const myDog = await imageLoaded('https://placedog.net/500');

    console.log('Whoa! This is now the time to enable my galery.');

    document.body.appendChild(myCat);
    document.body.appendChild(myDog);
}

runExample();

Vous auriez également pu attendre que toutes les images soient chargées.

async function runExample() {
    const [myCat, myDog] = [
        await imageLoaded('https://placekitten.com/500'),
        await imageLoaded('https://placedog.net/500')
    ];

    document.body.appendChild(myCat);
    document.body.appendChild(myDog);
}

Ou utilisez-les Promise.allpour les charger en parallèle.

async function runExample() {
    const [myCat, myDog] = await Promise.all([
        imageLoaded('https://placekitten.com/500'),
        imageLoaded('https://placedog.net/500')
    ]);

    document.body.appendChild(myCat);
    document.body.appendChild(myDog);
}

En savoir plus sur les promesses .

En savoir plus sur les fonctions "Async" .

En savoir plus sur la mission de déstructuration .

En savoir plus sur ECMAScript 2015 .

En savoir plus sur ECMAScript 2017 .


2

Je peux confirmer que l'approche de la question est suffisante pour déclencher les images à télécharger et à mettre en cache (sauf si vous avez interdit au navigateur de le faire via vos en-têtes de réponse) dans, au moins:

  • Chrome 74
  • Safari 12
  • Firefox 66
  • Edge 17

Pour tester cela, j'ai fait une petite webapp avec plusieurs points de terminaison qui dorment chacun pendant 10 secondes avant de servir une photo d'un chaton. J'ai ensuite ajouté deux pages Web, dont l'une contenait une <script>balise dans laquelle chacun des chatons est préchargé à l'aide de la preloadImagefonction de la question, et l' autre comprend tous les chatons de la page à l'aide de <img>balises.

Dans tous les navigateurs ci-dessus, j'ai constaté que si je visitais d'abord la page du préchargeur, attendais un moment, puis accédais à la page avec les <img>balises, mes chatons restituaient instantanément. Cela montre que le préchargeur a réussi à charger les chatons dans le cache de tous les navigateurs testés.

Vous pouvez voir ou essayer l'application que j'ai utilisée pour tester cela sur https://github.com/ExplodingCabbage/preloadImage-test .

Notez en particulier que cette technique fonctionne dans les navigateurs ci-dessus même si le nombre d'images en boucle dépasse le nombre de requêtes parallèles que le navigateur est prêt à faire à la fois, contrairement à ce que suggère la réponse de Robin . Le taux de préchargement de vos images sera bien sûr limité par le nombre de demandes parallèles que le navigateur est prêt à envoyer, mais il demandera éventuellement chaque URL d'image que vous appelez preloadImage().


Est-il important que les images soient mises en cache en mémoire ou sur disque? il semble que la méthode OP soit mise en cache sur le disque contrairement à la méthode @clintgh qui est mise en cache en mémoire.
Chris L

1

Vous pouvez déplacer ce code dans index.html pour précharger les images à partir de n'importe quelle URL

<link rel="preload" href="https://via.placeholder.com/160" as="image">

0

Le navigateur fonctionnera mieux en utilisant la balise de lien dans la tête.

export function preloadImages (imageSources: string[]): void {
  imageSources
    .forEach(i => {
      const linkEl = document.createElement('link');
      linkEl.setAttribute('rel', 'preload');
      linkEl.setAttribute('href', i);
      linkEl.setAttribute('as', 'image');
      document.head.appendChild(linkEl);
    });
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.