DEV Community

korywka
korywka

Posted on • Edited on

The most native image gallery

Hi, I want to share with you an approach to create the most native web gallery. By native, I mean a lot of work is done by the browser itself with minimum code (630B gzip). The gallery relies on some of the features that are supported by the latest versions of browsers (excluding experimental ones). It also corresponds to the principle of graceful degradation: to work in older browsers but lose some functionality.

Alt Text

The gallery is named <native-gallery> to take advantage of Custom Elements in the future. For now, it is just a custom HTML tag.

There are no strict requirements for HTML markup, so let's keep it as simple as possible:

<native-gallery>
  <img src="1.jpg" width="1600" height="900" alt="">
  <img src="2.jpg" width="675" height="900" alt="" loading="lazy">
  <img src="3.jpg" width="1600" height="900" alt="" loading="lazy">
  <img src="4.jpg" width="1600" height="900" alt="" loading="lazy">
  <img src="5.jpg" width="1600" height="900" alt="" loading="lazy">
</native-gallery>
Enter fullscreen mode Exit fullscreen mode

Lazy loading 🚀

The first modern feature used here is Native lazy loading with loading="lazy" attribute. There was some strange behaviour for a horizontal container (not window) scroll that all images were loading at the beginning anyway. I got around this by hiding and showing images back after page renders with these few lines:

/* -loaded class is set at initialization */
native-gallery:not(.-loaded) [loading="lazy"] {
    display: none;
}
Enter fullscreen mode Exit fullscreen mode

So the loading attribute is omitted in the first image tag for rendering it before the initialization of the plugin.

Since lazy loading is an important feature for production usage, lazysizes.js is supported as a polyfill.

Magnetic behaviour 🧲

The magnetic behaviour is implemented using Scroll Snap CSS property. Only a single image is visible after scroll:

native-gallery {
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
}

native-gallery img {
    scroll-snap-align: center;
}
Enter fullscreen mode Exit fullscreen mode

Mixed sizes images are supported too 🧙‍♂️

Any browser that does not support this feature will scroll the gallery with the standard behaviour.

Controls and events ⌨️

Controls and events are almost the only reason why this gallery contains javascript code. I tried to keep them as simple as possible. To navigate to the next image we need to preload it first:

function preloadImage(image) {
  if (image.complete) {
    return Promise.resolve();
  }
  return new Promise((resolve, reject) => {
    image.setAttribute('loading', 'eager');
    image.addEventListener('load', () => resolve());
    image.addEventListener('error', () => reject(Error(`can't load image: ${image.src}`)));
  });
}
Enter fullscreen mode Exit fullscreen mode

And then just to scroll container to the new position:

preloadImage(nextImage)
  .then(() => {
    root.scrollTo(root.offsetWidth * toIndex, 0);
  })
  .catch(error => console.error(error));
Enter fullscreen mode Exit fullscreen mode

To listen to change events, we subscribe to the container's scroll event through the throttle function for better performance.

Controls demo:

Alt Text

TL;DR 🏃‍♂️

Code repository / Example

Any feedback is much appreciated ❤️

Top comments (0)