lazysizes include extension/plugin asynchronously include non crucial content, styles or JS modules. Due to lazyloading, prioritized queuing and preload after load techniques lazySizes include extension scales much better than similar other solutions.
Typical use cases are:
- lazy loading different content, styles or JS modules depending on certain conditions (responsive content, responsive behavior, media queries, existence of a DOM element, browser features, user preferences, element queries...)
- deferring heavy to render or uncacheable content (client or server side)
- progressively enhancing the document with new JS enabled content
- splitting or deferred loading of large JS/CSS modules in larger projects
- clean and simple architecture for initialization/loading and/or destroying/unloading of (conditional) JS behaviors
- deferred self-initializing of DOM behaviors
Put a data-include
attribute on your .lazyload
element and reference the URL to load:
<aside class="related-articles lazyload" data-include="related-articles.html?contentid=32">
</aside>
The data-include
can also consume a list of candidates represented by a URL in combination with an optional parenthesised condition. The lazySizes include extension will then take the first URL, that matches the condition:
<div class="dynamic-content lazyload"
data-include="large.html (min-width: 800px), small.html">
</div>
The condition is either a media query or the name of a configured condition rule. The condition rule can be configured through CSS or through JS via the include.conditions
option.
The Lazysizes include extension checks wether the condition name matches the CSS content
value the of :after
or :before
pseudo elements from the html
element (can be configured).
<style>
html:after {
display: none;
content: "small";
}
@media (min-width: 760px){
html:after {
content: "medium";
}
}
@media (min-width: 980px){
html:after {
content: "large";
}
}
</style>
<div class="dynamic-content lazyload"
data-include="large.html (large), medium.html (medium), small.html">
</div>
A condition can also be configured through the lazySizesConfig.include.conditions
option map. Each key is either a string representing a media query or a function:
<script>
window.lazySizesConfig = {
include: {
conditions: {
small: '(max-width: 480px)',
custom: function(element, data){
return true || false;
}
}
}
};
</script>
<div class="dynamic-content lazyload"
data-include="small.html (small), custom.html (custom), default.html">
</div>
If the last include candidate has a condition, the innerHTML
of the initial content is used as unconditioned fallback content.
<div class="dynamic-content lazyload"
data-include="large.html (min-width: 800px)">
<!-- initial content + content for (max-width 799px) -->
</div>
As soon as the content is changed a lazyincluded
event is fired at the element:
<script>
document.addEventListener('lazyincluded', function(e){
// e.target has new content
});
</script>
<div class="dynamic-content lazyload" data-include="small.html">
</div>
The include feature can also load CSS, AMD or ES6 modules. To mark an URL as CSS put a css:
, to load an AMD module put a amd:
or to load an ES6 module put a module:
identifier in front of the URL:
<div class="dynamic-content lazyload" data-include="css:my-style.css (large)">
</div>
<div class="slider lazyload" data-include="amd:path/slider-module">
</div>
Content, Style and AMD includes can also be mixed and used with or without conditions:
<div class="slider lazyload"
data-include="slider.html amd:path/slider-module css:slider.css (large),
module:path/mobile-slider, css:mobile-slider.css">
</div>
In case content and a behavior include is used together lazySizes will load them in parallel but makes sure to first include the content and then initialize the behavior.
While you can write your AMD/ES6 module how you want lazysizes include extension will check wether your module provides the following methods:
yourmodule.lazytransform
: Will be invoked before the content is inserted. Especially to transform the AJAX response. (For example JSON to HTML)yourmodule.lazyload
: Callback function to initialize the module. Will be invoked after the content was inserted.yourmodule.lazyunload
: Callback function to destroy your widget. Will be invoked before old content is removed
Each of those methods are optional static methods of a module. Here is a simple example:
define(function(){
// constructor
var Slider = function(element,options) {
};
Slider.prototype = {
destroy: function(){}
};
// lazysizes include features:
// called with the DOM element (data.target) and some other useful data (data.detail)
// useful to initialize with the DOM element
Slider.lazyload = function(data){
var Slider = new Slider(data.target);
// save instance for destroy / lazyunload
// data.element._slider = Slider;
};
// in case of a conditioned include and
// the need to destroy the instance (i.e.: unbind global events)
Slider.lazyunload = function(data){
// data.target._slider.destroy();
};
// gets invoked with the a simplified XHR object (data.detail)
// and the dom element (data.target)
Slider.lazytransform = function(data){
// var json = JSON.parse(data.detail.responseText);
// data.detail.response = template(json);
};
return Slider;
});
In case a candidate includes new markup while another candidate only includes an AMD behavior. The initial content will be automatically resetted in case of a condition switch:
<div class="lazyload" data-include="slider.html amd:js/slider (big),
amd:js/mobileSlider">
<!-- amd:mobileSlider always works with the initial content and never on the slider.html include -->
</div>
In case both candidates has an amd module but not a content include the markup won't be resetted automatically. But the unloading module can request this behavior inside of it's unload method or the to initialized module can request this inside the lazytransform
method using the resetHTML
propety:
Slider.lazyunload = function(data){
// data.element._slider.destroy();
data.resetHTML = true;
};
//or
NewSlider.lazytransform = function(data){
// data.element._slider.destroy();
data.resetHTML = true;
};
In case the content doesn't contain any mutable states, that need to be transferred to the new behavior (i.e. input fields etc.), this option makes it extremley simple to cleanup the HTML for the next module.
The data object is shared between the lazyunload
, lazytransform
and lazyload
so that a possible state can be transferred.
Multiple styles or AMD modules for one candidate can be configured by separating them with |,|
signs:
<div class="slider lazyload"
data-include="amd:path/uiModule|,|path/slider-module css:slider-core.css|,|slider.css (large),
amd:path/mobile-slider, css:slider-core.css|,|mobile-slider.css">
</div>
The include feature will always use a download queue to make sure, that multiple includes do not jam the browsers own request queue. In case of many non crucial includes mixed with some crucial includes on one page the data-lazyqueue
attribute can be used to add a queue priority for the include extension:
<li>
<a href="">Category 1
(articles:
<span class="lazyload" data-include="count-service?id=32" data-lazyqueue=""></span>
)
</a>
</li>
<li>
<a href="">Category 2
(articles:
<span class="lazyload" data-include="count-service?id=33" data-lazyqueue=""></span>
)
</a>
</li>
<li>
<a href="">Category 3
(articles:
<span class="lazyload" data-include="count-service?id=34" data-lazyqueue=""></span>
)
</a>
</li>
<!-- more lazyqueued elements -->
<div class="lazyload" data-include="large.html (min-width: 800px), default.html">
<!-- crucial conditional content -->
</div>
lazyincludeload
is a cancelable event fired at the element before the request is started. Theevent.detail
object can be used to modify the XHR request.lazyincludeloaded
is a cancelable event fired at the element after the request is complete, but before the content is added. Theevent.detail.content
property can be used to modify the content (for example to transform JSON to HTML).lazyincluded
is an event fired at the element right after the HTML was injected.
Here are some examples:
const component = document.querySelector('.lazy-component');
// Modify the request headers when the targeted component kicks off an XHR
component.addEventListener('lazyincludeload', function(event) {
if (event.detail) {
event.detail.xhrModifier = function(request, candidate) {
request.setRequestHeader('Accept', 'application/json');
}
}
});
// Transform a JSON response into HTML before it is injected
component.addEventListener('lazyincludeloaded', function(event) {
if (event.detail.content) {
var json = JSON.parse(event.detail.content);
var html = '<h1>' + json.title + '</h1>';
html += '<div>' + json.body + '</div>';
event.detail.content = html;
}
});
All include options are configurable through the lazySizesConfig.include
option object:
The selector of the element, which should be used to check for the CSS content value:
window.lazySizesConfig = {
include: {
contentElement: '#mediaqueries' // 'html'
}
};
The conditions option can be used to create new custom conditions.
window.lazySizesConfig = {
addClasses: true, // good to add loading styles
include: {
conditions: {
small: '(max-width: 480px)',
custom: function(elem, data){
return true || false;
}
}
}
};
The map
option allows to map the value of the data-include
attribute to another string. This does not only work for the hole value, but also for parsed parts.
<script>
window.lazySizesConfig = {
addClasses: true, // good to add loading styles
include: {
map: {
slider: 'slider.html amd:path/slider (large)',
amdSlider: 'jquery|,|path/ui-slick'
}
}
};
</script>
<div class="lazyload" data-include="slider"></div>
<div class="lazyload" data-include="amd:amdSlider"></div>
This option becomes useful to separate content from behavior.
The include feature works together with all normal lazySizes options (i.e.: addClasses
for load indicators), events and methods.
Of course it is also possible to react to a user interaction.
<!-- markup has data-include, but no lazyload class -->
<div class="dynamic-content" data-include="include.html">
<button type="button" class="load-include">load content</button>
</div>
<script>
$(document).on('click', '.load-include', function(){
$(this)
//set button to disabled
.prop('disabled', true)
//get parent include area
.closest('[data-include]')
// and activate lazySizes by adding lazyload class
.addClass('lazyload')
;
});
</script>
It's also possible to change the data-include
value and reevaluate it:
<div class="dynamic-content lazyload" data-include="include.html amd:module (min-width: 800px)">
<button type="button" class="load-include" data-setinclude="include.html amd:module">load content</button>
</div>
<script>
$(document).on('click', '.load-include', function(){
$(this)
//set button to disabled
.prop('disabled', true)
//get parent include area
.closest('[data-include]')
//change data-include value
.attr('data-include', $.attr(this, 'data-setinclude'))
// and activate/refresh lazySizes by re-adding lazyload class
.addClass('lazyload')
;
});
</script>
<nav class="lazyload" data-include="amd:js/nav (big),
amd:js/mobileNav">
<!-- content -->
</nav>
define(function(){
// constructor
var Nav = function(element,options) {
};
Nav.prototype = {
};
Nav.lazyunload = function(data){
//Reset HTML for mobileNav
data.resetHTML = true;
//share the value of the input field with mobileNav
data.shareState = {
searchValue: $('input.search', data.target).val();
};
};
Nav.lazyload = function(data){
//check wether mobileNav has shared some data
if(data.shareState){
$('input.search', data.target).val(data.shareState.searchValue || '');
}
};
return Nav;
});
Note: For amd require JS or SystemJS to work the requireJs
and/or the systemJs
option has to be provided:
window.lazySizesConfig = window.lazySizesConfig || {};
window.lazySizesConfig.requireJs = function(modules, cb){
window.require(modules, cb);
};
window.lazySizesConfig.systemJs = function(module, cb){
window.System.import(module).then(cb);
};