Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent Bootstrap from preloading all the hidden-* images

I'm running Twitter Bootstrap 3.2.

My page has a banner image for every breakpoint. That means code like this:

<img class="visible-xs img-responsive" src="headline-768px.jpg">   (128kb)
<img class="visible-sm img-responsive" src="headline-992px.jpg">   (144kb)
<img class="visible-md img-responsive" src="headline-1200px.jpg">  (264kb)
<img class="visible-lg img-responsive" src="headline-2000px.jpg">  (380kb)

The main purpose of having 4 different images is so that mobile devices don't need to download the larger versions. But Bootstrap preloads all the versions regardless of screen size, even if they're hidden at the current breakpoint. This amounts to hundreds of unnecessary kb for every viewer.

Is there a way around this?

like image 615
Heraldmonkey Avatar asked Sep 11 '14 16:09

Heraldmonkey


People also ask

Are hidden images loaded?

Background images don't get loaded if the element is hidden.

Does display none stop loading?

Images or wrapper elements that have display: none set, will still get loaded. So if you want to eg. load a different image for desktop and mobile, you can either: use display: none , but with the loading="lazy" parameter set on img .

Should I preload images?

Preload can substantially improve LCP, especially if you need critical images (like Hero images) to be prioritized over the loading of other images on a page. While browsers will try their best to prioritize the loading of images in the visible viewport, <link rel=preload> can offer a significant boost in priority.

What is image preload?

Preload lets you tell the browser about critical resources that you want to load as soon as possible, before they are discovered in HTML. This is especially useful for resources that are not easily discoverable, such as fonts included in stylesheets, background images, or resources loaded from a script.


2 Answers

Demo of the Problem:

Not all browsers react the same, but typically browser's will load content from a src attribute, regardless of whether some other CSS rule renders that element as invisible.

Here's a baseline example:

<img class="visible-xs img-responsive" src="http://placehold.it/768x150" />
<img class="visible-sm img-responsive" src="http://placehold.it/992x150" />
<img class="visible-md img-responsive" src="http://placehold.it/1200x150"/>
<img class="visible-lg img-responsive" src="http://placehold.it/2000x150"/>

Demo in jsFiddle

Whenever I refresh the page, the browser loads all images, even ones that are initially invisible:

load-all

Background Image + Media Queries

This tutorial on Simple Responsive Images With CSS Background Images goes into more depth, but the basic solution works like this:

Create a simple div:

<div class="responsiveBackgroundImage" id="myImage" ></div>

And then sub in the correct background image depending on the screen size:

We'll probably want all the background images to share a couple basic properties like this:

.responsiveBackgroundImage { 
    width:100%;
    background-size: 100%;
    background-position: 50% 0%;
    background-repeat: no-repeat;
}   

Then replace the background-image property for this particular element at each resolution:

#myImage {background-image: url(http://placehold.it/768x150); }
@media (min-width: 768px)  { #myImage { background-image: url(http://placehold.it/992x150); }}
@media (min-width: 992px)  { #myImage { background-image: url(http://placehold.it/1200x150);}}
@media (min-width: 1200px) { #myImage { background-image: url(http://placehold.it/2000x150);}}

Demo in jsFiddle

Replacing Images with Javascript

Due to limitations with using background images, you might decide to use a real img element and then just dynamically load the images. Rather than loading the element itself, you can do something similar to what angular does with ng-src when evaluating an expression that resolves to a url. When the browser loads each element, it wouldn't be able to find the file location of src="{{personImageUrl}}". Instead, Angular saves the intended url as a data attribute and then loads when appropriate.

First, take each of your images and prefix the src attribute with data-. Now the browser won't automatically start implementing anything, but we still have the information to work with.

<img class="visible-xs img-responsive" data-src="http://placehold.it/768x150"/>

Then query for all the dynamic images we'll eventually want to load.

$dynamicImages = $("[data-src]");

Next, we're going to want to capture window resize events, and on the very first page load, we'll trigger one just so we can make use of the same code. Use a function like this:

$(window).resize(function() {
    // Code Goes Here
}).resize();

Then check if each dynamic image is visible. If so, load the image from the data attribute and remove it from the array so we don't have to keep checking and loading it.

$dynamicImages.each(function(index) {
    if ($(this).css('display') !== 'none') {
        this.src = $(this).data('src');
        $dynamicImages.splice(index, 1)
    }
});

Demo in jsFiddle

like image 137
KyleMit Avatar answered Oct 13 '22 01:10

KyleMit


it is not bootstrap, but the browser. You should use background images (instead of <img> tags) together with media queries.

A proper browser will load only the css background image matching the media query, but it makes sense for it to load all images in the markup regardless of their css styling

(Necessary pain until <picture> will be widely supported)

like image 31
Luca Avatar answered Oct 13 '22 02:10

Luca