Lightweight lazy loading built on IntersectionObserver. Zero dependencies.
<img class="g--lazy-01" data-src="photo.jpg" alt="..." />
import Lazy from '@terrahq/lazy'; const lazy = new Lazy();
.g--lazy-01 {
opacity: 0;
transition: opacity 0.4s ease;
}
.g--lazy-01--is-active {
opacity: 1;
}
<!-- Any non-img/video/iframe element uses background-image --> <div class="g--lazy-01 hero" data-src="hero.jpg"></div> // Lazy preloads with new Image(), then applies: // element.style.backgroundImage = url("hero.jpg")
import Lazy from '@terrahq/lazy'; const lazy = new Lazy();
<!-- Direct src --> <video class="g--lazy-01" data-src="clip.mp4" muted autoplay loop playsinline></video> <!-- Multiple sources --> <video class="g--lazy-01" muted autoplay loop playsinline> <source data-src="clip.webm" type="video/webm" /> <source data-src="clip.mp4" type="video/mp4" /> </video>
import Lazy from '@terrahq/lazy'; const lazy = new Lazy();
<iframe class="g--lazy-01" data-src="https://www.youtube.com/embed/VIDEO_ID"></iframe>
import Lazy from '@terrahq/lazy'; const lazy = new Lazy();
<button id="btn">Load image</button> <img id="hero" class="g--lazy-01" data-src="hero.jpg" alt="..." />
import Lazy from '@terrahq/lazy'; // Create instance then immediately destroy observer const lazy = new Lazy({ selector: '.g--lazy-01', }); lazy.destroy(); // no auto-loading // Load on click document.querySelector('#btn').addEventListener('click', () => { lazy.load(document.querySelector('#hero')); });
<div class="marquee" id="marquee"> <div class="marquee-item"> <img class="g--lazy-01" data-src="image.jpg" alt="..." /> </div> <!-- more items... --> </div>
import Lazy from '@terrahq/lazy'; import gsap from 'gsap'; import { horizontalLoop } from '@andresclua/infinite-marquee-gsap'; const lazy = new Lazy(); const el = document.querySelector('#marquee'); const loop = horizontalLoop(el.children, { paused: false, repeat: -1, speed: 1, }); // Pause on hover el.addEventListener('mouseenter', () => { gsap.to(loop, { timeScale: 0, overwrite: true }); }); el.addEventListener('mouseleave', () => { gsap.to(loop, { timeScale: 1, overwrite: true }); }); // IntersectionObserver picks up items as GSAP // transforms move them into the viewport. No revalidate() needed.
.marquee {
display: flex;
overflow: hidden;
height: 250px;
}
.marquee-item {
flex-shrink: 0;
width: 350px;
height: 100%;
}
<div id="slider"> <div class="slider-item"> <img class="g--lazy-01" data-src="slide.jpg" alt="..." /> </div> <!-- more slides... --> </div>
import Lazy from '@terrahq/lazy'; import { tns } from 'tiny-slider'; const lazy = new Lazy(); const slider = tns({ container: '#slider', items: 3, loop: false, }); // After each slide transition, revalidate so newly // visible slides get observed and loaded slider.events.on('transitionEnd', () => { lazy.revalidate(); });
const lazy = new Lazy({ onError: (el) => { console.warn('Failed to load:', el); }, onComplete: () => { console.log('All elements processed (success + errors)'); }, });
/* Added automatically via errorClass */
.g--lazy-01--is-error {
opacity: 1;
outline: 2px solid red;
}