0Pricing
HTML Academy · Lesson

Lazy Loading with IntersectionObserver

Defer image and content loading until they enter the viewport.

Lazy Loading with IntersectionObserver is a free HTML Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the HTML Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Beyond Native loading="lazy"

Native lazy loading covers <img> and <iframe>. For anything else — background images, video, expensive components, third-party widgets — IntersectionObserver is the right tool to defer work until the element is near the viewport.

The Pattern

Mark deferred elements with a data-src attribute holding the real source; render a tiny placeholder. Observe each one; on intersection, swap data-src to src (or otherwise activate the element) and unobserve so the callback never fires again.

<img class="lazy" data-src="/photo.jpg" alt="Photo" width="800" height="600">
<script>
const observer = new IntersectionObserver((entries) => {
  for (const entry of entries) {
    if (entry.isIntersecting) {
      entry.target.src = entry.target.dataset.src;
      observer.unobserve(entry.target);
    }
  }
}, { rootMargin: "200px" });
document.querySelectorAll(".lazy").forEach((img) => observer.observe(img));
</script>

Lazy Background Images

CSS background-image cannot be lazy-loaded natively. On intersection, set el.style.backgroundImage = `url("${el.dataset.bg}")`, or add a CSS class that defines the background-image. The image starts downloading at that moment.

Lazy Video

For below-the-fold videos, omit src and source elements initially. On intersection, inject the <source> tags and call video.load(). Without this, video metadata may download eagerly even before the user scrolls anywhere near the element.

Third-Party Widgets

Social media embeds, comment widgets, and chat scripts pull in megabytes. On intersection, dynamically import the widget's setup script: await import("/embed.js"). The cost is paid only when the user actually scrolls to the section.

Lazy Iframe (Beyond loading="lazy")

Native iframe lazy loading still downloads the iframe document. For heavy embeds (maps, video players), use a "facade" pattern: show a placeholder image; on click or intersection, replace it with the real iframe. Reduces initial page weight dramatically.

rootMargin for Pre-Loading

rootMargin: "200px" starts loading 200px before the element enters the viewport. Tune this based on scroll speed: too small and users see placeholders during fast scrolls; too large and you defeat the lazy-load purpose by loading everything.

Unobserve on Trigger

Always observer.unobserve(element) after the lazy load activates. Otherwise the callback continues firing every time the element re-enters the viewport — wasted CPU and potential bugs if the activation is not idempotent.

Reserve Space

Set width/height on lazy images and a min-height on lazy containers. Without explicit dimensions, the page reflows as content loads — annoying scrolling jumps and a bad Cumulative Layout Shift score.

Fallback for No-JS

For full Progressive Enhancement, wrap the lazy element in <noscript> with the real src: <noscript><img src="/photo.jpg"></noscript>. Users without JS get the image eagerly; users with JS get the lazy version. Pair with proper data-src markup.

Batching Many Elements

A single observer instance can watch hundreds of elements efficiently. Do not create one observer per element. Create one shared observer with the desired options and use observe() to register each target.

Combining with Native loading="lazy"

For <img> specifically, use loading="lazy" as the primary mechanism and reserve IntersectionObserver for cases native lazy loading does not cover. The two compose cleanly: native handles below-the-fold images; IntersectionObserver handles backgrounds, components and other custom loaders.

Knowledge Check

Why is it important to call observer.unobserve(element) after a lazy-loaded element has been activated?

Summary

IntersectionObserver extends lazy loading beyond native <img loading="lazy"> to backgrounds, video, third-party widgets and dynamic component imports. Use data-src placeholders, swap on intersection, unobserve after activation. Pre-load with rootMargin, reserve space with width/height, provide noscript fallback, and combine with native lazy loading where it applies.

Frequently asked questions

Is the “Lazy Loading with IntersectionObserver” lesson free?

Yes — the full text of “Lazy Loading with IntersectionObserver” is free to read here on the web, and the HTML Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the HTML Academy course, upgrade to CoddyKit PRO.

What will I learn in “Lazy Loading with IntersectionObserver”?

Defer image and content loading until they enter the viewport. You practise HTML Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start HTML Academy?

No prior experience is required. HTML Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Lazy Loading with IntersectionObserver” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this HTML Academy lesson?

Yes. Every HTML Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. IntersectionObserver API and Thresholds
  2. Lazy Loading with IntersectionObserver
  3. Infinite Scroll Implementation
  4. Scroll-Linked Animations Setup
← Back to HTML Academy