The author:maxin, prohibit reprinting without authorization.
preface
There are usually three ways to determine whether an element enters the viewport
1. el.offsetTop - document.documentElement.scrollTop <= viewPortHeight
2. el.getBoundingClientRect().top <= viewPortHeight
3. intersectionRatio > 0 && intersectionRatio <= 1
Copy the code
Depending on whether the element intersects with the viewport, you can perform operations such as top suction, bottom suction, exposure reporting, list loading more, picture lazy loading, etc.
The problem
The first two need to listen for scroll events. In order to prevent frequent triggering, anti-shake processing is needed.
The user experience
- When an element enters the viewport, there is always a delay before the judgment logic can be executed.
performance
- Running on the main thread, frequent firing and calls can cause performance problems
- Whether or not the intersection is triggered, a judgment is made after the scroll ends
- To obtain
srollTop
The value of andgetBoundingClientRect
Methods lead tobackflow
- Scrolling events bind multiple event handlers, blocking UI rendering
IntersectionObserver
The callback is executed when the target element and the root element intersect or disintersect, and no task is running during the current tick
The constructor
let observer = new IntersectionObserver(callback, options);
Copy the code
callback
Each entry describes an observed change in the intersection of the target element
let callback = (entries, observer) = > {
entries.forEach(entry= > {
// entry.boundingClientRect
// The region information of the target element, the return value of getBoundingClientRect()
// entry.intersectionRatio
// The visible scale of the target element
// entry.intersectionRect
// Information about the area where the target element intersects the root element
// entry.isIntersecting
// Whether the target element enters the root element region
// entry.rootBounds
// The rectangle area of the root element
// entry.target
// Observe the DOM node
// entry.time
// The number of milliseconds since the intersection occurred when the page opened
});
};
Copy the code
options
root
Root element that does not specify Windows by defaultrootMargin
The margin of the root elementthreshold
The target element intersects the root element at this ratio to trigger a callback
Instance methods
observe
Start listening for a target element,target
Must be a descendant of rootunobserve
Stop listening on a target elementtakeRecords
Returns the collection of all listening target elementsdisconnect
Stop all listening
reference
MDN Web Docs – Intersection Observer API
Vue Directive optimizes lazy loading
steps
Instead of loading the image directly, store the image address in data-src
<img
class="image-item"
:data-src="imageUrl"
v-lazyload
/>
Copy the code
LazyLoadDirective.js
export default {
// hookFunction is called when the binding element is inserted into the parent node
insert(el) {
function loadImage() {
el.addEventListener('load'.() = > {
// A lazy addition of the class after loading can be used to animate the fade
setTimeout(() = > {
el.classList.add('loaded')},100);
});
// Load the image address of data-url
el.src = el.dataset.url;
}
function handleIntersect(entries, observer) {
entries.forEach(entry= > {
if(! entry.isIntersecting) {return;
} else {
// When the binding element enters the viewport, the image is loaded
loadImage();
// And stop observing changes in visibility to prevent reloading of the image.observer.unobserve(el); }}); }function createObserver() {
const options = {
root: null.threshold: '0'
};
const observer = new IntersectionObserver(handleIntersect, options);
// Subscribe to view the currently bound image elementobserver.observe(el); } createObserver(); }}Copy the code
thinking
If scroll event is used to judge whether an image is visible or not, and dozens of images need to be recalculated each time, it is obviously more elegant to use IntersectionObserver.
Registration instructions
// main.js global registration
import Vue from 'vue';
import LazyLoadDirective from '@/directives/LazyLoadDirective';
Vue.directive('lazyload', LazyLoadDirective);
Copy the code
summary
It is very simple to implement delayed loading using IntersectionObserver, which has good performance and user experience. At present, system compatibility covers a wide range. Of course, long list optimization is also required if the list element nodes are particularly large.
At the end
Thank you for reading. Recently, the big front end team of ZHicyun Health is participating in the nuggets popular team contest. If you think that’s good, then come and vote for us!
A total of 21 votes can be cast today, 7 votes on web page, 7 votes on app.com and 7 votes on Share. Thanks for your support, we will be creating more technical articles in 2021
Your support is our biggest motivation ~