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 obtainsrollTopThe value of andgetBoundingClientRectMethods 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

  • rootRoot element that does not specify Windows by default
  • rootMarginThe margin of the root element
  • thresholdThe target element intersects the root element at this ratio to trigger a callback

Instance methods

  • observeStart listening for a target element,targetMust be a descendant of root
  • unobserveStop listening on a target element
  • takeRecordsReturns the collection of all listening target elements
  • disconnectStop 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 ~