Gallery

by Jesse Johnson

HTML

<h1>Gallery Example</h1>
<div class="gallery">
    <img data-src="https://picsum.photos/800/600.webp?random=1" alt="Gallery Image 1">
    <img data-src="https://picsum.photos/800/600.webp?random=2" alt="Gallery Image 2">
    <img data-src="https://picsum.photos/800/600.webp?random=3" alt="Gallery Image 3">
    <img data-src="https://picsum.photos/800/600.webp?random=4" alt="Gallery Image 4">
    <img data-src="https://picsum.photos/800/600.webp?random=5" alt="Gallery Image 5">
    <img data-src="https://picsum.photos/800/600.webp?random=6" alt="Gallery Image 6">
    <img data-src="https://picsum.photos/800/600.webp?random=7" alt="Gallery Image 7">
    <img data-src="https://picsum.photos/800/600.webp?random=8" alt="Gallery Image 8">
    <img data-src="https://picsum.photos/800/600.webp?random=9" alt="Gallery Image 9">
    <img data-src="https://picsum.photos/800/600.webp?random=10" alt="Gallery Image 10">
    <img data-src="https://picsum.photos/800/600.webp?random=11" alt="Gallery Image 11">
    <img data-src="https://picsum.photos/800/600.webp?random=12" alt="Gallery Image 12">
    <img data-src="https://picsum.photos/800/600.webp?random=13" alt="Gallery Image 13">
    <img data-src="https://picsum.photos/800/600.webp?random=14" alt="Gallery Image 14">
    <img data-src="https://picsum.photos/800/600.webp?random=15" alt="Gallery Image 15">
    <img data-src="https://picsum.photos/800/600.webp?random=16" alt="Gallery Image 16">
    <img data-src="https://picsum.photos/800/600.webp?random=17" alt="Gallery Image 17">
    <img data-src="https://picsum.photos/800/600.webp?random=18" alt="Gallery Image 18">
    <img data-src="https://picsum.photos/800/600.webp?random=19" alt="Gallery Image 19">
    <img data-src="https://picsum.photos/800/600.webp?random=20" alt="Gallery Image 20">
    <img data-src="https://picsum.photos/800/600.webp?random=21" alt="Gallery Image 21">
    <img data-src="https://picsum.photos/800/600.webp?random=22" alt="Gallery Image 22">
    <img...

CSS

body {
  margin: 0;
  font-family: "Georgia", serif;
  background-color: #fff;
  color: #333;
  text-align: center;
  padding: 2em;
}

h1 {
  font-size: 1.8em;
  margin-bottom: 1.5em;
  font-weight: normal;
}

.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  gap: 20px;
  max-width: 1000px;
  margin: 0 auto;
}

.gallery img {
  width: 100%;
  opacity: 0;
  transition: opacity 1.2s ease-in-out;
  border-radius: 8px;
}

.gallery img.loaded {
  opacity: 1;
}

JavaScript

document.addEventListener("DOMContentLoaded", () => {
  const images = document.querySelectorAll(".gallery img")

  const observer = new IntersectionObserver(
    (entries, obs) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          const img = entry.target
          img.src = img.dataset.src

          img.onload = () => {
            img.classList.add("loaded")
          }

          obs.unobserve(img)
        }
      })
    },
    {
      threshold: 0.1,
    },
  )

  images.forEach((img) => observer.observe(img))
})