Enhancement
Lazysrc
Lazy image loading built on the browser's own loading="lazy"
- Import
@parallelogram-js/core/components/Lazysrc- Selector
[data-lazysrc]:not([data-lazysrc-complete])- Stylesheet
@parallelogram-js/core/styles/lazysrc.css
Playground
Real sources work without JavaScript; Lazysrc adds native lazy loading.
<img data-lazysrc
src="images/harbour-640.jpg"
srcset="images/harbour-640.jpg 640w, images/harbour-1280.jpg 1280w"
sizes="(max-width: 640px) 100vw, 640px"
width="640" height="400" alt="Harbour at dawn">
Use the example to see the events it sends.
Loads through an observer shortly before it scrolls into view.
Book a table by the water
<div class="hero" data-lazysrc data-lazysrc-bg="images/harbour-1280.jpg">
<p>Book a table by the water</p>
</div>
Use the example to see the events it sends.
Book a table by the water
About
The browser decides when images load. Markup that already has a real src or srcset is never stripped, so it works without JavaScript; Lazysrc only adds loading="lazy" and decoding="async" when they are missing. Sources held in data attributes, on the image or on the <source> elements of its <picture>, are copied onto the elements as soon as the image mounts. Without JavaScript those images stay empty, so follow them with a <noscript> copy that has real sources, such as <noscript><img src="harbour.jpg" alt="Harbour at dawn"></noscript>. Background images have no native lazy loading, so they load through an IntersectionObserver shortly before they scroll into view.
Failed loads are retried with a growing delay. Don't lazy load the largest image above the fold: give it a plain src and fetchpriority="high" instead.
Usage
Register the component and the framework loads it the first time a page contains [data-lazysrc]:not([data-lazysrc-complete]).
import { Parallelogram } from '@parallelogram-js/core';
const app = Parallelogram.create();
app.components.add('[data-lazysrc]:not([data-lazysrc-complete])', () => import('@parallelogram-js/core/components/Lazysrc'));
app.run();
The framework is optional. Lazysrc.enhanceAll() mounts the component on every matching element by itself, for a page that wants one enhancement rather than a framework:
import Lazysrc from '@parallelogram-js/core/components/Lazysrc';
Lazysrc.enhanceAll();
It takes a selector and the component's options, and returns the instance it mounted them with. What it leaves out is what the framework adds: loading a component only on the pages that use it, mounting again after a page swap, and ordering with dependsOn.
Its styles come from the package stylesheet:
@import '@parallelogram-js/core/styles/lazysrc.css';
Attributes
| Name | Type | Default | Description |
|---|---|---|---|
data-lazysrc | flag | none | Marks an image, or an element with a background image |
data-lazysrc-src | url | none | Source copied onto the image |
data-lazysrc-srcset | string | none | Source set copied onto the image or a <source> |
data-lazysrc-sizes | string | none | Sizes copied onto the image or a <source> |
data-lazysrc-bg | url | none | Background image, loaded shortly before the element scrolls into view |
data-lazysrc-fetchpriority | high | low | auto | none | Copied to the image's fetchpriority |
data-lazysrc-root-margin | string | 600px 0px | How far outside the viewport background images start loading For every instance, Lazysrc.defaults.rootMargin. |
data-lazysrc-threshold | number | 0 | Visible fraction at which a background image starts loading For every instance, Lazysrc.defaults.threshold. |
data-lazysrc-retry-attempts | number | 3 | Retries after a failed load For every instance, Lazysrc.defaults.retryAttempts. |
data-lazysrc-retry-delay | number | 1000 | Milliseconds before the first retry, multiplied by the attempt number after For every instance, Lazysrc.defaults.retryDelay. |
data-lazysrc-fade-duration | number | 300 | Fade length in milliseconds, set as --lazy-transition-duration For every instance, Lazysrc.defaults.fadeInDuration. |
data-lazysrc-loading-class | string | lazysrc--loading | Class added while loading For every instance, Lazysrc.defaults.loadingClass. |
data-lazysrc-loaded-class | string | lazysrc--loaded | Class added once loaded For every instance, Lazysrc.defaults.loadedClass. |
data-lazysrc-error-class | string | lazysrc--error | Class added when every retry failed For every instance, Lazysrc.defaults.errorClass. |
data-lazysrc-stateset by the component | loading | loaded | error | none | Loading progress |
Events
| Name | Detail | Description |
|---|---|---|
lazysrc:mounted | { element: HTMLElement; config: object; timestamp: number } | The element was set up DOM event and event bus |
lazysrc:loading-start | { element: HTMLElement; timestamp: number } | Sources were handed to the browser, or a background image started loading DOM event and event bus |
lazysrc:loaded | { element: HTMLElement; loadTime: number | null; timestamp: number } | The image loaded; loadTime is the download time from Resource Timing, or null when the browser has no entry for it DOM event and event bus |
lazysrc:error | { element: HTMLElement; error: string; timestamp: number } | Every retry failed, or the element has no source to load DOM event and event bus |
lazysrc:detached | { element: HTMLElement; timestamp: number } | A loaded element's listeners were released DOM event and event bus |
lazysrc:forceLoad | none | Dispatch this on an element to load it straight away You dispatch this. |
CSS custom properties
| Name | Default | Description |
|---|---|---|
--lazy-transition-duration | 0.3s | Length of the fade |
--lazy-transition-easing | none | Easing of the fade |
--lazy-loading-opacity | 0.7 | Opacity while loading |
--lazy-placeholder-bg | var(--color-surface-muted) | Background while loading: the muted surface |
--lazy-error-bg | var(--color-danger-tint) | Background after an error: the danger tint |
--lazy-error-color | var(--color-danger) | Outline colour after an error: the danger colour |