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?
Background images don't get loaded if the element is hidden.
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 .
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.
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.
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:
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
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
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With