Boosting Web Performance: Mastering Lazy Loading Techniques for Optimal User Experience
Draft Disclaimer: Please note that this article is currently in draft form and may undergo revisions before final publication. The content, including information, opinions, and recommendations, is subject to change and may not represent the final version. We appreciate your understanding and patience as we work to refine and improve the quality of this article. Your feedback is valuable in shaping the final release.
Lazy load them all
description: Lazy load them all: only load what you need by the time you need it tags: beginners, webdev, tutorial, productivity, seo, javascript
Lazy loading in general
- Definition on the series intro
- Helper to lazy load anything
import hash from 'object-hash'
/**
* Idempotent lazy load script
* @param {object} options
* @param {string} [options.type]
* @param {string} [options.id]
* @returns {Promise}
*/
export function lazyLoad (options) {
return new Promise((resolve, reject) => {
if (!options.type) {
return reject(new Error('LazyLoad is expecting { type: string } as argument'))
}
if (!options.id) {
options.id = `${options.type}${hash(options)}`
}
let element = document.querySelector(`#${options.id}`)
if (element) {
console.warn(`Element ${options.type} you are trying to lazy load already exists with id = ${options.id}`)
return resolve(element)
}
element = document.createElement(options.type)
Object.keys(options).forEach(key => {
if (key !== 'type') {
element.setAttribute(key, options[key])
}
})
element.addEventListener('load', () => {
resolve(element)
})
element.addEventListener('error', (e) => {
reject(e)
})
document.body.append(element)
})
}
Scripts & Styles
Scripts
/**
* @param options
* @returns {Promise}
*/
export function lazyLoadScript (options) {
return lazyLoad({
type: 'script',
...options
})
}
Styles
/**
* @param options
* @returns {Promise}
*/
export function lazyLoadCss (options) {
return lazyLoad({
rel: 'stylesheet',
type: 'link',
...options
})
}
Perspectives
- Todo:
- Use an element on the page to know when to load the
- Alternatives
- prefetch
<link rel="prefetch" href="<your-resource>"/>
- prefetch
Note that the lazyLoad
function we defined above can be used to lazy load anything you want ie video, audio and so on.
Page loads matter, as well as other metrics such as:
- first contentfull paint Those metrics would make or break your user experience. Improving them could set you apart from your competition.
Images
Image lazy loading and their implications your SEO score
If you are always trying to reduce load time 10ms to 100ms, if optimizing your algorithms is important to you then this is for you because today I will be sharing with you how you can enhance your UI/UX by lazy loading your image
This can be part of
- Be at the top of your SEO game
- Your SEO rank performance +10
- Image lazy loading and their implication
Summary
- how image are fetched ?
how images are fetched ?
- get a page [insert:visual] [insert:visual] as graph
- browser gets sub resources (css, js, img) for you [insert:visual] as graph [insert:visual]
[insert:page: ten sections page]
Sum up: You are asking for one page but you're doing (10 + 1) network requests
First improvement attempt
- defer, async
Defer or loading the image content of our site can improve our first render
- improve load time
- bots care about content and how fast it is delivered
- first paint, first meaningful paint
- important for SEO
How to ?
Load the image only when needed, only when image come into view. No need to load something we may not need.
Listener to window scroll
- not viable solution. Too much computing
// get the elements once page loads
let $elements = document.querySelectorAll('.element')
window.addEventListener('scroll', () => {
$elements.forEach($element => {
if ($element.getBoundingClientRect().top < window.innerHeight) {
// element is visible
$element.setAttribute('src', $element.getAttribute('data-src'))
$element.addEventListener('load', () => {
// element is now available
// add class for nice css transition
})
$element.addEventListener('error', () => {
// may be interesting to catch this error when network image is not available or failed to load for any server issue
})
$element.removeAttribute('data-src')
}
})
})
Listener to window scroll - improvement
Debounce listener to scroll.
Use Web api new InteractionObserver
Realize that even when all image are loaded, we are still listening to the window scroll. This is when interaction observer come in handy with interaction observer
const defaultOptions = {
attribute: 'data-src',
onVisibleClass: 'visible',
removeOnVisible: true,
}
options = Object.assign({}, defaultOptions, options)
const io = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// element is visible
const element = entry.target
const src = element.getAttribute(options.attribute)
element.setAttribute('src', src)
element.classList.add(options.onVisibleClass)
if (options.removeOnVisible) {
observer.disconnect()
}
}
})
})
const $element = options.selector instanceof HTMLElement ? options.selector : document.querySelector(options.selector)
io.observe($element)
Wrap up
- pros
- improve first render time
- cons
- UI wise problem: flickering
- reflow coming from image render. Image is taking space it did not have in first place since it had no width nor height
- solution needs javascript
- no javascript then you're content won't show
- UI wise problem: flickering
Comments
This also applies to reviews or any non critical information on page load.
If you have a blog with comments at the bottom, or any other dynamic contents that are not meaningful to your content, then you should consider lazy loading it. That of course apply to a comment section of a blog article. Besides, you don't necessarily want your comments showing up on search engine like Google as coming from you
Insert Amazing blog post content with comment that is hateful
This is also applicable to your reviews section if you are an ecommerce website
General advices
- bundle your resources (css, js, img, fonts) in one file - If you're using font-awesome or glyphicons you can use sprite to group all your images
- otherwise lazy load them - only load them when need
Wrap up
- pros
- improve first render time
- cons
- UI wise problem: flickering
- reflow coming from image render. Image is taking space it did not have in first place since it had no width nor height
- solution needs javascript
- no javascript then you're content won't show
- UI wise problem: flickering