Odo Responsive Images
Native responsive images with deferred loading.
The main purpose of this component is to lazy-load responsive images. After deciding when to load the image, it uses picturefill and native responsive images. This component does not control which sources are chosen for the image.
Support
IE9+ (with a classList
polyfill).
Dependencies
Picturefill, Odo Viewport, debounce (bundled), Array.from
.
Styles
This component has required css - css/odo-responsive-images.css.
Usage
Images are not immediately watched, you must call initialize()
. This allows you to change the OdoResponsiveImages
class before anything meaningful happens.
import OdoResponsiveImages from '@odopod/odo-responsive-images';
OdoResponsiveImages.initialize();
Deferred Loading
To defer the loading, do not use a <picture>
element. Use a <div>
and add the odo-responsive-img
class.
Markup
IE9 Conditional Comments
To support IE9, you will need to wrap a video
element wrapper around the source elements in your picture tag. You can do this using conditional comments, like so:
<div class="odo-responsive-img">
<!--[if IE 9]><video style="display: none;"><![endif]-->
<source srcset="https://source.unsplash.com/category/buildings/800x400" media="(min-width: 992px)">
<source srcset="https://source.unsplash.com/category/technology/600x300" media="(min-width: 768px)">
<source srcset="https://source.unsplash.com/category/nature/400x200">
<!--[if IE 9]></video><![endif]-->
<img alt="picture 1 description">
<noscript>
<img src="https://source.unsplash.com/category/nature/400x200" alt="picture 1 description">
</noscript>
</div>
Avoid Multiple Image Downloads
In order to avoid downloading more than one image for a single picture, the <img>
’s src
and srcset
attributes are left blank. If src
is set, any browser which does not support <picture>
could potentially download multiple images. If srcset
is set, any browser which supports srcset
but does not support <picture>
could potentially download multiple images.
However, this means in order to support JavaScript-disabled browsers (and possibly search engines), you will need a fallback image wrapped in <noscript>
. Please note, the <noscript>
is not added to the generated <picture>
element.
Hi Resolution Support
To support retina screens, simply modify the srcset
attribute. The Opera developer blog has a great article with many different combinations for images sizes, hi-dpi screens, mime-types, and art direction. I highly recommend looking at that article to give you an idea of how to structure your responsive image.
Here is a simple example using the 2x
descriptor in the srcset
. On a non-retina screen, you will see an image from the “architecture”, “animals”, or “nature” categories of placeimg depending on your screen size. For retina screens, you will see the same categories in grayscale.
<div class="odo-responsive-img">
<!--[if IE 9]><video style="display: none;"><![endif]-->
<source srcset="https://placeimg.com/800/400/arch 1x, https://placeimg.com/1600/800/arch/grayscale 2x" media="(min-width: 992px)">
<source srcset="https://placeimg.com/600/300/animals 1x, https://placeimg.com/1200/600/animals/grayscale 2x" media="(min-width: 768px)">
<source srcset="https://placeimg.com/400/200/nature 1x, https://placeimg.com/800/400/nature/grayscale 2x">
<!--[if IE 9]></video><![endif]-->
<img alt="picture 1 description">
<noscript>
<img src="https://placeimg.com/400/200/nature" alt="picture 1 description">
</noscript>
</div>
Background Images
You can add a data-type="background"
attribute to set visibility hidden on the <img>
element and have the responsive image component set the background-image
property of your responsive image to that image's source.
<div class="odo-responsive-img" data-type="background">
<!--[if IE 9]><video style="display: none;"><![endif]-->
<source srcset="https://placeimg.com/800/400/arch" media="(min-width: 992px)">
<source srcset="https://placeimg.com/600/300/animals" media="(min-width: 768px)">
<source srcset="https://placeimg.com/400/200/nature">
<!--[if IE 9]></video><![endif]-->
<img alt="picture 2 description">
<noscript>
<img src="https://placeimg.com/400/200/nature" alt="picture 1 description">
</noscript>
</div>
Using img[srcset]
Odo Responsive Images also work with the native srcset
attribute for responsive images. The key difference is you must use a data-srcset
attribute instead of srcset
. This also works with the data-type="background"
attribute.
Since JavaScript adds the srcset
attribute, there should also be a <noscript>
with a fallback image.
<div class="odo-responsive-img">
<img data-srcset="
https://placehold.it/400x200/e74c3c/ffffff&text=srcset+-+400x200 400w,
https://placehold.it/600x300/e74c3c/ffffff&text=srcset+-+600x300 600w,
https://placehold.it/800x400/e74c3c/ffffff&text=srcset+-+800x400 800w,
https://placehold.it/1200x600/e74c3c/ffffff&text=srcset+-+1200x600 1200w" sizes="(min-width:1291px) 1200px, 93vw" alt="I have a source set">
<noscript>
<img src="https://placehold.it/400x200/e74c3c/ffffff&text=srcset+-+400x200" alt="I have a source set">
</noscript>
</div>
Simple Carousel
Demonstrating how the responsive images will work inside a carousel and similar components with off-screen images.
Immediate Loading
To load responsive images without deferred loading, simply use the native picture
syntax. If you check the network profile for this page load, you will notice that these two images load first.
Picture Syntax
Picture should be used for responsive images which require art-direction. For example, an image set which has different ratios or crops at different sizes.
<picture>
<!--[if IE 9]><video style="display: none;"><![endif]-->
<source srcset="https://placeimg.com/800/400/arch" media="(min-width: 992px)">
<source srcset="https://placeimg.com/600/300/animals" media="(min-width: 768px)">
<source srcset="https://placeimg.com/400/200/nature">
<!--[if IE 9]></video><![endif]-->
<img alt="picture 1 description">
</picture>
Srcset Syntax
Each image in a source set should be the exact same, except at a different size/scale. This allows the browser to choose which image is best for the user given its current conditions (i.e. network, pixel density, signal strength).
<img srcset="
https://placehold.it/400x200&text=srcset+-+400x200 400w,
https://placehold.it/600x300&text=srcset+-+600x300 600w,
https://placehold.it/800x400&text=srcset+-+800x400 800w" sizes="50vw" alt="I have a source set">
Public Methods & Properties
OdoResponsiveImages
is a singleton. It exposes a few methods and properties which can be useful.
updateOffsets()
If the offset of an image changes without the page scrolling, OdoViewport
will not know and neither will this component. A responsive image inside a carousel, for example, will need to be notified when the slide changes. This method is debounced at 300ms.
OdoResponsiveImages.updateOffsets();
flush()
Cleans up all references and listeners for current images. OdoResponsiveImages
will no longer be doing anything until a new image is added.
OdoResponsiveImages.flush();
add(element)
Add more images for the responsive images component to watch. The first parameter is an element or array of elements. The element should be the parent element of the <img>
.
OdoResponsiveImages.add(myResponsiveImage);
remove(element)
Remove images from the responsive images component. The first parameter is an element or array of elements. The element should be the parent element of the <img>
. This method is useful when you are completely removing content from the DOM.
OdoResponsiveImages.remove(myResponsiveImage);
load(element)
Force the load of an element or group of elements instead of waiting for it to come into the viewport. The first parameter is an element or array of elements. The element should be the parent element of the <img>
.
OdoResponsiveImages.load(myResponsiveImage);
images
An array of viewport item id strings and picture elements. Once an image loads, it is removed from this list unless it is a background image.
OdoResponsiveImages.images
ClassName
The class name strings for Odo Responsive Images.
OdoResponsiveImages.ClassName = {
IMAGE: 'odo-responsive-img',
LOADED: 'odo-responsive-img--loaded'
}