Preloading images with JavaScript

后端 未结 14 1045
太阳男子
太阳男子 2020-11-22 03:17

Is the function I wrote below enough to preload images in most, if not all, browsers commonly used today?

function preloadImage(url)
{
    var img=new Image(         


        
相关标签:
14条回答
  • 2020-11-22 03:38

    Yes this will work, however browsers will limit(between 4-8) the actual calls and thus not cache/preload all desired images.

    A better way to do this is to call onload before using the image like so:

    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;
    }
    
    0 讨论(0)
  • 2020-11-22 03:40

    I can confirm that the approach in the question is sufficient to trigger the images to be downloaded and cached (unless you have forbidden the browser from doing so via your response headers) in, at least:

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

    To test this, I made a small webapp with several endpoints that each sleep for 10 seconds before serving a picture of a kitten. Then I added two webpages, one of which contained a <script> tag in which each of the kittens is preloaded using the preloadImage function from the question, and the other of which includes all the kittens on the page using <img> tags.

    In all the browsers above, I found that if I visited the preloader page first, waited a while, and then went to the page with the <img> tags, my kittens rendered instantly. This demonstrates that the preloader successfully loaded the kittens into the cache in all browsers tested.

    You can see or try out the application I used to test this at https://github.com/ExplodingCabbage/preloadImage-test.

    Note in particular that this technique works in the browsers above even if the number of images being looped over exceeds the number of parallel requests that the browser is willing to make at a time, contrary to what Robin's answer suggests. The rate at which your images preload will of course be limited by how many parallel requests the browser is willing to send, but it will eventually request each image URL you call preloadImage() on.

    0 讨论(0)
  • 2020-11-22 03:42

    I recommend you use a try/catch to prevent some possible issues:

    OOP:

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

    Standard:

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

    Also, while I love DOM, old stupid browsers may have problems with you using DOM, so avoid it altogether IMHO contrary to freedev's contribution. Image() has better support in old trash browsers.

    0 讨论(0)
  • 2020-11-22 03:45

    Solution for ECMAScript 2017 compliant browsers

    Note: this will also work if you are using a transpiler like 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();
    

    You could also have waited for all images to load.

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

    Or use Promise.all to load them in parallel.

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

    More about Promises.

    More about "Async" functions.

    More about the destructuring assignment.

    More about ECMAScript 2015.

    More about ECMAScript 2017.

    0 讨论(0)
  • 2020-11-22 03:49

    CSS2 Alternative: 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; 
    }
    

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

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

    NOTE: Images in a container with display:none might not preload. Perhaps visibility:hidden will work better but I have not tested this. Thanks Marco Del Valle for pointing this out

    0 讨论(0)
  • 2020-11-22 03:50

    Working solution as of 2020

    Most answers on this post no longer work - (atleast on Firefox)

    Here's my solution:

    var cache = document.createElement("CACHE");
    cache.style = "position:absolute;z-index:-1000;opacity:0;";
    document.body.appendChild(cache);
    function preloadImage(url) {
        var img = new Image();
        img.src = url;
        img.style = "position:absolute";
        cache.appendChild(img);
    }
    

    Usage:

    preloadImage("example.com/yourimage.png");
    

    Obviously <cache> is not a "defined" element, so you could use a <div> if you wanted to.

    Use this in your CSS, instead of applying the style attribute:

    cache {
        position: absolute;
        z-index: -1000;
        opacity: 0;
    }
    
    cache image {
        position: absolute;
    }
    

    If you have tested this, please leave a comment.

    Notes:

    • Do NOT apply display: none; to cache - this will not load the image.
    • Don't resize the image element, as this will also affect the quality of the loaded image when you come to use it.
    • Setting position: absolute to the image is necessary, as the image elements will eventually make it's way outside of the viewport - causing them to not load, and affect performance.

    UPDATE

    While above solution works, here's a small update I made to structure it nicely:

    (This also now accepts multiple images in one function)

    var cache = document.createElement("CACHE");
    document.body.appendChild(cache);
    function preloadImage() {
        for (var i=0; i<arguments.length; i++) {
            var img = new Image();
            img.src = arguments[i];
            var parent = arguments[i].split("/")[1]; // Set to index of folder name
            if ($(`cache #${parent}`).length == 0) {
                var ele = document.createElement("DIV");
                ele.id = parent;
                cache.appendChild(ele);
            }
            $(`cache #${parent}`)[0].appendChild(img);
            console.log(parent);
        }
    }
    
    preloadImage(
        "assets/office/58.png",
        "assets/leftbutton/124.png",
        "assets/leftbutton/125.png",
        "assets/leftbutton/130.png",
        "assets/leftbutton/122.png",
        "assets/leftbutton/124.png"
    );
    

    Preview:

    Notes:

    • Try not to keep too many images preloaded at the same time (this can cause major performance issues) - I got around this by hiding images, which I knew wasn't going to be visible during certain events. Then, of course, show them again when I needed it.
    0 讨论(0)
提交回复
热议问题