Don't load hidden images

I have a bunch of hidden images on my website. Their container DIVs have style="display: none". Depending on the user's actions, some images may be revealed via javascript. The problem is that all my images are loaded upon opening the page. I would like to put less strain on the server by only loading images that eventually become visible. I was wondering if there was a pure CSS way to do this. Here are two hacky/complicated ways I am currently doing it. As you can see, it's not clean code.

<div id="hiddenDiv">
   <img src="spacer.gif" />
</div>

.reveal .img {
 background-image: url(flower.png);
}

$('hiddenDiv').addClassName('reveal');

Here is method 2:

<img id="flower" fakeSrc="flower.png" />

function revealImage(id) {
 $('id').writeAttribute(
  'src',
  $('id').readAttribute('fakeSrc')
 );
}

revealImage('flower');

Answers:

Answer

Here's a jQuery solution:

$(function () {
   $("img").not(":visible").each(function () {
       $(this).data("src", this.src);
       this.src = "";
   });

   var reveal = function (selector) {
       var img = $(selector);
       img[0].src = img.data("src");
   }
});

It's similar to your solution, except it doesn't use the fakeSrc attribute in the markup. It clears the src attribute to stop it from loading and stores it elsewhere. Once you are ready to show the image, you use the reveal function much like you do in your solution. I apologize if you do not use jQuery, but the process should be transferable to whichever framework (if any) that you use.

Note: This code specifically must be ran before the window has fired the load event but after the DOM has been loaded.

Answer

The browser will load any images that has a src attribute set, so what you want to do is to use data-src in the markup and use JavaScript to set the src attribute when you want it to load.

<img class="hidden" data-src="url/to/image.jpg" />

I created this tiny plugin that will take care of the problem:

(function($){
    // Bind the function to global jQuery object.
    $.fn.reveal = function(){
        // Arguments is a variable which is available within all functions
        // and returns an object which holds the arguments passed.
        // It is not really an array, so by calling Array.prototype
        // he is actually casting it into an array.
        var args = Array.prototype.slice.call(arguments);

        // For each elements that matches the selector:
        return this.each(function(){
            // this is the dom element, so encapsulate the element with jQuery.
            var img = $(this),
                src = img.data("src");

            // If there is a data-src attribute, set the attribute
            // src to make load the image and bind an onload event.
            src && img.attr("src", src).load(function(){
                // Call the first argument passed (like fadeIn, slideIn, default is 'show').
                // This piece is like doing img.fadeIn(1000) but in a more dynamic way.
                img[args[0]||"show"].apply(img, args.splice(1));
            });
        });
    }
}(jQuery));

Execute .reveal on the image(s) you want to load/show:

$("img.hidden").reveal("fadeIn", 1000);

See test case on jsFiddle.

Answer

It partially depends on how your images must be placed in your code. Are you able to display the images as the background of a <div>, or are you required to use the <img> tag? If you need the <img> tag, you may be screwed depending on the browser being used. Some browsers are smart enough to recognize when an image is inside of a hidden object or in an object of 0 width/height and not load it since it's essentially invisible, anyway. For this reason many people will suggest putting an image in a 1x1 pixel <span> if you want the image to be pre-loaded but not visible.

If you don't require the <img> tag, most browsers won't load images referenced by CSS until the element in question becomes visible.

Mind you that short of using AJAX to download the image there's no way to be 100% sure the browser won't pre-load the image anyway. It's not unbelievable that a browser would want to pre-load anything it assumes may be used later in order to "speed up" the average load times.

Answer

Using CSS to put the image an unused class, then adding that class to an element with javascript is going to be your best bet. If you don't use image tags at all, this solution becomes a bit more obvious.

Though, for perspective, most people have the opposite problem where they want to preload an image so it shows up instantly when it's told to be shown.

Answer

If you are okay relying on scripting, there is the background image method and the image src method. Put simply, set all your hidden images to some very small image (reduce strain on server) or one that does not exist at all (who cares? The visitor cannot see the image-missing [X] anyway, the div is hidden) then change it with script...

<img src="I_do_not_exist.jpg" id="my_image" />
<input type="button" onclick="document.getElementById('my_image').src='I_exist.jpg';" Value="change image" />

<br /><br /><br />

<div id="mydiv" style="width:40px; height:40px; border:2px solid blue"></div>

<input type="button" onclick="document.getElementById('my_div').style.width='455px';document.getElementById('my_div').style.height='75px';document.getElementById('my_div').style.backgroundImage='url(I_exist.jpg)';" Value="change background image" />

I left a width on the above example to show that nothing is in the div image wise until you ask it to load.

Answer

If you make the image a background-image of a div in CSS, when that div is set to 'display: none', the image will not load.

You can do the following for a pure CSS solution, it also makes the img box actually behave like an img box in a responsive design setting (that's what the transparent png is for), which is especially useful if your design uses responsive-dynamically-resizing images.

<img style="display: none; height: auto; width:100%; background-image: 
url('img/1078x501_1.jpg'); background-size: cover;" class="center-block 
visible-lg-block" src="img/400x186_trans.png" alt="pic 1">

The image will only be loaded when the media query tied to visible-lg-block is triggered and display:none is changed to display:block. The transparent png is used to allow the browser to set appropriate height:width ratios for your <img> block (and thus the background-image) in a fluid design (height: auto; width: 100%).

1078/501 = ~2.15  (large screen)
400/186  = ~2.15  (small screen)

So you end up with something like the following, for 3 different viewports:

<img style="display: none; height: auto; width:100%; background-image: url('img/1078x501_1.jpg'); background-size: cover;" class="center-block visible-lg-block" src="img/400x186_trans.png" alt="pic 1">
<img style="display: none; height: auto; width:100%; background-image: url('img/517x240_1.jpg'); background-size: cover;" class="center-block visible-md-block" src="img/400x186_trans.png" alt="pic 1">
<img style="display: none; height: auto; width:100%; background-image: url('img/400x186_1.jpg'); background-size: cover;" class="center-block visible-sm-block" src="img/400x186_trans.png" alt="pic 1">

And only your default media viewport size images load during the initial load, then afterwards, depending on your viewport, images will dynamically load.

And no javascript!

Answer

Weirdly, there's no answer about native lazy loading which is implemented in the majority of the browsers already.

you can do it by adding loading="lazy" attribute to your image.

Addy Osmani wrote a great article about it. You can read more about lazy loading here: https://addyosmani.com/blog/lazy-loading/

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.