Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Intersection Observer API and its application #163

Open
reboottime opened this issue Sep 28, 2023 · 4 comments
Open

Intersection Observer API and its application #163

reboottime opened this issue Sep 28, 2023 · 4 comments

Comments

@reboottime
Copy link
Owner

reboottime commented Sep 28, 2023

Overview

This article talks about the application of Intersection Observer API and its compatibility consideration.

The basic API

const options = {
  root: document.querySelector("#scrollArea"),
  rootMargin: "0px",
  threshold: 1.0,
};

let observer = new IntersectionObserver(callback, options);
  • The threshold: A threshold of 1.0 means that when 100% of the target is visible within the element specified by the root option, the callback is invoked.
  • The callback:
let callback = (entries, observer) => {
  entries.forEach((entry) => {
    // Each entry describes an intersection change for one observed
    // target element:
    //   entry.boundingClientRect
    //   entry.intersectionRatio
    //   entry.intersectionRect
    //   entry.isIntersecting
    //   entry.rootBounds
    //   entry.target
    //   entry.time
  });
};
@reboottime
Copy link
Owner Author

reboottime commented Oct 12, 2023

Intersection Observer API Applications


Application on Image Lazy loading

  • The script to load img programmatically
import { useState, useEffect, useRef } from 'react';

export default function useLoadImg() {
    const [state, setState] = useState<'loading' | 'loaded' | 'error'>();
    const loadFuncRef = useRef<CallableFunction>(emptyFunc);

    useEffect(() => {
        loadFuncRef.current = async (src: string) => {
            setState('loading');

            try {
                await loadImg(src);
                setState('loaded');
            } catch (e) {
                setState('error');
            }
        }
    }, []);

    return {
        state,
        loadImg: loadFuncRef.current
    };
}

function loadImg(src: string) {
    return new Promise((resolve, reject) => {
        const img = new Image();
        img.onload = () => {
            resolve(void);
        };
        img.onerror = () => {
            reject();
        }
    });
};

function emptyFunc (...args:any[]) {
}
  • The Component to load img in a lazy way
const PLACEHOLDER_IMG_URL = '';

const LazyImage = ({ src, alt, className }) => {
  const imgRef = useRef(null);
  const { state: imgState, loadImg } = useLoadImg();

  useEffect(() => {
    const viewportHeight = window.innerHeight;

    const observer = new IntersectionObserver((entries) => {
      entries.forEach(entry => {
        if (entry.boundingClientRect.top < 1.5 * viewportHeight) {
          loadImg(src);
        }
      });
    });
    const imgElem = imgRef.current;

    if (imgElem) {
      observer.observe(imgElem);
    }

    return () => {
      if (imgElem) {
        observer.unobserve(imgElem);
      }
    };
  }, [loadImg, src]);

  const isVisible = imgState === 'loaded';

  return (
    <img
      alt={alt}
      ref={imgRef}
      src={isVisible ? src : PLACEHOLDER_IMG_URL}
    />
  );
};

@reboottime
Copy link
Owner Author

reboottime commented Oct 12, 2023

Application on Infinite loading

Use case: Implementing "infinite scrolling" websites, where more and more content is loaded and rendered as you scroll, so that the user doesn't have to flip through pages.

import React, { useEffect, useRef, useState } from 'react';

const InfiniteScrollComponent: React.FC = () => {
  const observer = useRef<IntersectionObserver | null>(null);
  const [page, setPage] = useState(1);
  const [isLoading, setIsLoading] = useState(false);
  const containerRef = useRef<HTMLDivElement | null>(null);

  useEffect(() => {
    // Initialize Intersection Observer when component mounts
    observer.current = new IntersectionObserver((entries) => {
      if (entries[0].isIntersecting) {
        // When the observed element is in view, load more content
        setIsLoading(true);
        setPage(prevPage => prevPage + 1);
      }
    });

    // Start observing the container element
    if (containerRef.current) {
      observer.current.observe(containerRef.current);
    }

    return () => {
      // Clean up observer when component unmounts
      if (observer.current) {
        observer.current.disconnect();
      }
    };
  }, []);

  useEffect(() => {
    // Simulate loading more content (you would fetch data from an API here)
    if (isLoading) {
      // Example fetch using window.fetch
      fetch(`https://api.example.com/data?page=${page}`)
        .then(response => response.json())
        .then(data => {
          // Handle the loaded data
          console.log('Loaded data:', data);
          setIsLoading(false);
        })
        .catch(error => {
          console.error('Error fetching data:', error);
          setIsLoading(false);
        });
    }
  }, [isLoading, page]);

  return (
    <div>
      <div>
        {/* Render your content here */}
        {/* ... */}
      </div>
      <div ref={containerRef}>
        {/* This is the element that will be observed */}
      </div>
    </div>
  );
};

export default InfiniteScrollComponent;

@reboottime
Copy link
Owner Author

reboottime commented Oct 12, 2023

Application on adding css animation to html element that just jumped in user's view

import React, { useRef, useEffect } from 'react';
import './AnimatedComponent.css'; // Create this file for your CSS animation

const AnimatedComponent = () => {
  const animatedRef = useRef(null);

  useEffect(() => {
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          entry.target.classList.add('animate'); // Add the CSS class for animation
          observer.unobserve(entry.target); // Stop observing once animation is applied
        }
      },
      { threshold: 0.1 } // Adjust threshold as needed
    );

    if (animatedRef.current) {
      observer.observe(animatedRef.current);
    }

    return () => {
      if (animatedRef.current) {
        observer.unobserve(animatedRef.current);
      }
    };
  }, []);

  return <div className="animated-element" ref={animatedRef}></div>;
};

export default AnimatedComponent;

@reboottime reboottime changed the title Intersection Observer API Intersection Observer API and its application Oct 12, 2023
@reboottime
Copy link
Owner Author

reboottime commented Oct 12, 2023

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant