How To Achieve Lazy Loading

Lazy loading is a must for any web page with a lot of information

Why lazy loading is needed

Usually when a user opens a web page, the entire content of the page will be downloaded and presented in a single page. Although browsers are allowed to cache the page, they cannot guarantee that the user will view all the downloaded content, for example, a photo wall application, the user may just view the first image and then leave, resulting in a waste of memory and bandwidth. So we need to load the content only when the user needs to access a part of the page, rather than loading the whole content at the beginning.

How to achieve lazy loading

When a resource is presented to a web page (image, video), the resource references a small placeholder, and when the user views the page, the actual resource is cached by the browser and the placeholder is replaced when the resource is visible on the screen, e.g., if the user loads the page and leaves the page immediately, nothing is loaded except for the top of the page.

Lazy loading specific implementation

To load an image as an example, we need to set a data-src attribute in the img tag, which points to the image we actually need to load, while the src of the img points to a default image, if it is empty, it will also send a request to the server.

<img src="default.jpg" data-src="www.example.com/img1.jpg">

Later, when the user accesses the img element of the visual area, the src value is replaced with the image loaded by the actual resource pointed to by data-src

const lazy = (el) => {
let scrTop = getTop();
let windowHeight = document.documentElement.clientHeight;
function getTop(){
return document.documentElement.scrollTop || document.body.scrollTop;
}
function getOffset(node){
return node.getBoundingClientRect().top + scrTop;
}
function inView(node){

const threshold = 0;
const viewTop = scrTop;
const viewBot = viewTop + windowHeight;

const nodeTop = getOffset(node);
const nodeBot = nodeTop + node.offsetHeight;

const offset = (threshold / 100) * windowHeight;
console.log((nodeBot >= viewTop - offset), (nodeTop <= viewBot + offset))
return (nodeBot >= viewTop - offset) && (nodeTop <= viewBot + offset)
}
function check(node){
let el = document.querySelector(node);
let images = [...el.querySelectorAll('img')];
images.forEach(img => {
if(inView(img)){
img.src = img.dataset.src;
}
})
}
check(el);
}

window.onscroll = function(){
lazy('.foo');
}

Modern lazy loading implementation method

Through the implementation of the above example, we need to listen to the scroll event in order to achieve lazy loading, although we can prevent the high frequency of the execution of the function by means of function throttling, but we still need to calculate the scrollTop, offsetHeight and other attributes, there is no simple way to not need to calculate these attributes, the answer is yes – IntersectionObserver

The IntersectionObserver API provides a way for developers to asynchronously listen for target elements that are in an intersection state with their ancestors or viewports. Ancestor elements and viewports are called roots.

It is simply a matter of observing whether an element overlaps with another element.

The IntersectionObserver initialization process provides configuration of three main elements.

  1. root: This is the root element used for observation. He defines the basic capture framework for observable elements. By default, root points to the browser’s viewport, but can actually be any DOM element, note that: root in this case, the element to be observed must be inside the Dom element represented by root.

2. rootMargin: A rectangular offset added to the root bounding box when calculating the intersection, which can be used to effectively narrow or widen the range of the root determination to meet the needs of the calculation. The options are similar to marginCSS, e.g. rootMargin: ‘50px 20px 10px 40px’(top, right, bottom, left)

3. threshold: A list of thresholds, sorted in ascending order, each threshold in the list is the ratio of the intersection area of the listener object to the boundary area. A Notification is generated when any threshold of the listener object is crossed. If no value is passed to the constructor, the default value is 0.

In order to tell the intersectionObserver what configuration we want, we just need to pass our config object to the Observer constructor along with our callback function.

const config = {
root: null,
rootMargin: '0px',
threshold: 0.5
}
let observer = new IntersectionObserver(fucntion(entries){
// ...
}, config)

Now we need to go and give the IntersectionObserver the actual elements to observe.

const img = document.querySelector('image');
observer.observe(img);

A few points to note about this actual observed element:

  • First he should be located in the DOM element represented by root
  • IntersectionObserver can only accept one observation element at a time and does not support bulk observations. This means that if you need to observe several elements (for example, several images on a page), you must iterate through all the elements and observe each of them separately.
const images = document.querySelecttorAll('img');
images.forEach(image => {
observer.observe(image)
})
  • When loading a page using Observer, you may notice that callbacks have been triggered for all elements observed by IntersectionObserver. We can solve this problem with the callback function

IntersectionObserver callback function

new IntersectionObserver(function(entries, self))

In entries we get our callback function as Array of special type: IntersectionObserverEntry First of all IntersectionObserverEntry contains the information of three different rectangles.

  • rootBounds: CaptureFrame(root + rootMargin) rectangle
  • boundClientRect: Observe the rectangle of the element itself
  • intersectionRect: Capture the rectangle where the frame and observation elements intersect.

In addition, IntersectionObserverEntry also provides isIntersecting, a convenient property that returns whether the observed element intersects the capture frame.
In addition, IntersectionObserverEntry provides the computationally useful traversal property intersctionRatio: returns the ratio of the intersectionRect to the boundingClientRect.

After a brief introduction, let’s get back to the main topic and use this IntersectionObserver to implement a modern lazy loading method:

const images = document.querySelectorAll('[data-src]')
const config = {
rootMargin: '0px',
threshold: 0
};
let observer = new IntersectionObserver((entries, self)=>{
entries.forEach(entry => {
if(entry.isIntersecting){
// Loading images
preloadImage(entry.target);
// Release from observation
self.unobserve(entry.target)
}
})
}, config)

images.forEach(image => {
observer.observe(image);
});

function preloadImage(img) {
const src = img.dataset.src
if (!src) { return; }
img.src = src;
}

Compared to the previous lazy loading method is not more concise, and only when the observation element and the capture frame cross or overlap, it will trigger the dropback function.

Benefits of Delayed Loading

  • Delayed loading strikes a balance between optimizing content loading and simplifying the end-user experience.
  • Users can load to the content faster because they only need to load a portion of the content when they first open the site.
  • The site sees higher user retention because the constant delivery of content to users reduces the chances of them leaving the site.
  • Sites see lower resource costs because content is loaded only when users need it, not all at once.

Level Up Coding

Thanks for being a part of our community! Before you go:

🚀👉 Join the Level Up talent collective and find an amazing job


How To Achieve Lazy Loading was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.


This content originally appeared on Level Up Coding - Medium and was authored by Maxwell

Lazy loading is a must for any web page with a lot of information

Why lazy loading is needed

Usually when a user opens a web page, the entire content of the page will be downloaded and presented in a single page. Although browsers are allowed to cache the page, they cannot guarantee that the user will view all the downloaded content, for example, a photo wall application, the user may just view the first image and then leave, resulting in a waste of memory and bandwidth. So we need to load the content only when the user needs to access a part of the page, rather than loading the whole content at the beginning.

How to achieve lazy loading

When a resource is presented to a web page (image, video), the resource references a small placeholder, and when the user views the page, the actual resource is cached by the browser and the placeholder is replaced when the resource is visible on the screen, e.g., if the user loads the page and leaves the page immediately, nothing is loaded except for the top of the page.

Lazy loading specific implementation

To load an image as an example, we need to set a data-src attribute in the img tag, which points to the image we actually need to load, while the src of the img points to a default image, if it is empty, it will also send a request to the server.

<img src="default.jpg" data-src="www.example.com/img1.jpg">

Later, when the user accesses the img element of the visual area, the src value is replaced with the image loaded by the actual resource pointed to by data-src

const lazy = (el) => {
let scrTop = getTop();
let windowHeight = document.documentElement.clientHeight;
function getTop(){
return document.documentElement.scrollTop || document.body.scrollTop;
}
function getOffset(node){
return node.getBoundingClientRect().top + scrTop;
}
function inView(node){

const threshold = 0;
const viewTop = scrTop;
const viewBot = viewTop + windowHeight;

const nodeTop = getOffset(node);
const nodeBot = nodeTop + node.offsetHeight;

const offset = (threshold / 100) * windowHeight;
console.log((nodeBot >= viewTop - offset), (nodeTop <= viewBot + offset))
return (nodeBot >= viewTop - offset) && (nodeTop <= viewBot + offset)
}
function check(node){
let el = document.querySelector(node);
let images = [...el.querySelectorAll('img')];
images.forEach(img => {
if(inView(img)){
img.src = img.dataset.src;
}
})
}
check(el);
}

window.onscroll = function(){
lazy('.foo');
}

Modern lazy loading implementation method

Through the implementation of the above example, we need to listen to the scroll event in order to achieve lazy loading, although we can prevent the high frequency of the execution of the function by means of function throttling, but we still need to calculate the scrollTop, offsetHeight and other attributes, there is no simple way to not need to calculate these attributes, the answer is yes - IntersectionObserver

The IntersectionObserver API provides a way for developers to asynchronously listen for target elements that are in an intersection state with their ancestors or viewports. Ancestor elements and viewports are called roots.

It is simply a matter of observing whether an element overlaps with another element.

The IntersectionObserver initialization process provides configuration of three main elements.

  1. root: This is the root element used for observation. He defines the basic capture framework for observable elements. By default, root points to the browser’s viewport, but can actually be any DOM element, note that: root in this case, the element to be observed must be inside the Dom element represented by root.

2. rootMargin: A rectangular offset added to the root bounding box when calculating the intersection, which can be used to effectively narrow or widen the range of the root determination to meet the needs of the calculation. The options are similar to marginCSS, e.g. rootMargin: ‘50px 20px 10px 40px’(top, right, bottom, left)

3. threshold: A list of thresholds, sorted in ascending order, each threshold in the list is the ratio of the intersection area of the listener object to the boundary area. A Notification is generated when any threshold of the listener object is crossed. If no value is passed to the constructor, the default value is 0.

In order to tell the intersectionObserver what configuration we want, we just need to pass our config object to the Observer constructor along with our callback function.

const config = {
root: null,
rootMargin: '0px',
threshold: 0.5
}
let observer = new IntersectionObserver(fucntion(entries){
// ...
}, config)

Now we need to go and give the IntersectionObserver the actual elements to observe.

const img = document.querySelector('image');
observer.observe(img);

A few points to note about this actual observed element:

  • First he should be located in the DOM element represented by root
  • IntersectionObserver can only accept one observation element at a time and does not support bulk observations. This means that if you need to observe several elements (for example, several images on a page), you must iterate through all the elements and observe each of them separately.
const images = document.querySelecttorAll('img');
images.forEach(image => {
observer.observe(image)
})
  • When loading a page using Observer, you may notice that callbacks have been triggered for all elements observed by IntersectionObserver. We can solve this problem with the callback function

IntersectionObserver callback function

new IntersectionObserver(function(entries, self))

In entries we get our callback function as Array of special type: IntersectionObserverEntry First of all IntersectionObserverEntry contains the information of three different rectangles.

  • rootBounds: CaptureFrame(root + rootMargin) rectangle
  • boundClientRect: Observe the rectangle of the element itself
  • intersectionRect: Capture the rectangle where the frame and observation elements intersect.

In addition, IntersectionObserverEntry also provides isIntersecting, a convenient property that returns whether the observed element intersects the capture frame.
In addition, IntersectionObserverEntry provides the computationally useful traversal property intersctionRatio: returns the ratio of the intersectionRect to the boundingClientRect.

After a brief introduction, let’s get back to the main topic and use this IntersectionObserver to implement a modern lazy loading method:

const images = document.querySelectorAll('[data-src]')
const config = {
rootMargin: '0px',
threshold: 0
};
let observer = new IntersectionObserver((entries, self)=>{
entries.forEach(entry => {
if(entry.isIntersecting){
// Loading images
preloadImage(entry.target);
// Release from observation
self.unobserve(entry.target)
}
})
}, config)

images.forEach(image => {
observer.observe(image);
});

function preloadImage(img) {
const src = img.dataset.src
if (!src) { return; }
img.src = src;
}

Compared to the previous lazy loading method is not more concise, and only when the observation element and the capture frame cross or overlap, it will trigger the dropback function.

Benefits of Delayed Loading

  • Delayed loading strikes a balance between optimizing content loading and simplifying the end-user experience.
  • Users can load to the content faster because they only need to load a portion of the content when they first open the site.
  • The site sees higher user retention because the constant delivery of content to users reduces the chances of them leaving the site.
  • Sites see lower resource costs because content is loaded only when users need it, not all at once.

Level Up Coding

Thanks for being a part of our community! Before you go:

🚀👉 Join the Level Up talent collective and find an amazing job


How To Achieve Lazy Loading was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.


This content originally appeared on Level Up Coding - Medium and was authored by Maxwell


Print Share Comment Cite Upload Translate Updates
APA

Maxwell | Sciencx (2022-11-29T12:40:16+00:00) How To Achieve Lazy Loading. Retrieved from https://www.scien.cx/2022/11/29/how-to-achieve-lazy-loading/

MLA
" » How To Achieve Lazy Loading." Maxwell | Sciencx - Tuesday November 29, 2022, https://www.scien.cx/2022/11/29/how-to-achieve-lazy-loading/
HARVARD
Maxwell | Sciencx Tuesday November 29, 2022 » How To Achieve Lazy Loading., viewed ,<https://www.scien.cx/2022/11/29/how-to-achieve-lazy-loading/>
VANCOUVER
Maxwell | Sciencx - » How To Achieve Lazy Loading. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2022/11/29/how-to-achieve-lazy-loading/
CHICAGO
" » How To Achieve Lazy Loading." Maxwell | Sciencx - Accessed . https://www.scien.cx/2022/11/29/how-to-achieve-lazy-loading/
IEEE
" » How To Achieve Lazy Loading." Maxwell | Sciencx [Online]. Available: https://www.scien.cx/2022/11/29/how-to-achieve-lazy-loading/. [Accessed: ]
rf:citation
» How To Achieve Lazy Loading | Maxwell | Sciencx | https://www.scien.cx/2022/11/29/how-to-achieve-lazy-loading/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.