DEV Community

Cover image for Web Component ideas: Building a carousel
Ben Taylor
Ben Taylor

Posted on

Web Component ideas: Building a carousel

Carousels are a useful component for displaying a series of images (or really any media). People often use carousels off the shelf, but they're quite easy to build yourself! In this post I'll go through building a Web Component for a carousel.

Screen Recording 2021-08-31 at 10.46.44 pm

You can see here that as you click the next and previous buttons it scrolls through a series of images.

Building elements that don't exist

Web Components are handy tools for abstracting out common patterns in HTML. There's lots of user interface patterns that are common on the web but don't have their own elements. Instead you have to build them up yourself from other elements.

If you find yourself repeating HTML, or the complexity gets too much, it can be handy to abstract out the complexity. By creating a Web Component you can create a neat abstraction that becomes reusable and easier to understand. Plus it makes your HTML much tidier!

What does the HTML look like?

This Web Component is rather simple, it doesn't have any attributes - you just put some elements inside it.

<ben-carousel>
  <img src="http://placekitten.com/360/200">
  <img src="http://placekitten.com/300/200">
  <img src="http://placekitten.com/420/200">
</ben-carousel>
Enter fullscreen mode Exit fullscreen mode

The idea here is to stick to standard elements as much as possible and to keep it simple. The carousel is going to be in charge of displaying the content like a carousel. Then the content itself can be anything! In this case I've put in three images of cats at different sizes.

A big benefit of this approach, using Web Components, is that I can put any HTML content inside my carousel. It's just HTML! Plus I can use it in any website, no matter the library. Sticking to standards often makes things easier.

Creating the template

To begin with I wrote down the elements I'd need to create this carousel:

<template id="ben-carousel">
  <button>Prev</button>
  <slot></slot>
  <button>Next</button>
</template>
Enter fullscreen mode Exit fullscreen mode

You can see here that I've got two buttons, one for going left and one for going right. Then I've put a slot element in, this is where the content inside will go. But this isn't quite enough for it to look like a carousel. Right now it looks like this:

Screen Shot 2021-08-31 at 11.09.30 pm

So we'll need to add some styling. Once we've got it looking like a carousel we can move on to making it work like a carousel.

Here's how I styled it:

<template id="ben-carousel">
  <style>
    :host {
      display: block;
    }

    #container {
      display: flex;
    }

    #images {
      display: flex;
      flex-shrink: 1;
      overflow: scroll;
    }
  </style>
  <div id="container">
    <button id="prev">Prev</button>
    <div id="images">
      <slot></slot>
    </div>
    <button id="next">Next</button>
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode

First lets look at the extra elements I added. I've created a div to wrap all of the other elements, this will be useful for layout. Then I've added another div for the images, this will help to control how much of them is displayed.

You can also see here that I've given every element an id. When you're working with Web Components the HTML and CSS is all scoped to within the component. So you can use id as much as you please, it won't overlap with others on your page. This makes it much easier to write JavaScript and CSS.

Now lets look at the style here. The first part makes it so the element displays as a block element:

:host {
  display: block;
}
Enter fullscreen mode Exit fullscreen mode

Next up I've styled the outer container to use display: flex:

#container {
  display: flex;
}
Enter fullscreen mode Exit fullscreen mode

This makes it so that the three child elements (#prev, #images, and #next) all display next to eachother horizontally. Which is key to the whole layout!

Finally we're looking at the #images container. Here I use flex-shrink: 1 to make it so the #images container will shrink down when the width of its parent is constrained.

#images {
  flex-shrink: 1;
  display: flex;
  overflow: scroll;
}
Enter fullscreen mode Exit fullscreen mode

I've also used display: flex here to make the children of the #images container (the images) all display next to eachother in a line. Finally overflow: scroll says that the #images container should create a scrollbar if its content has overflowed it.

The result looks like this:

Screen Shot 2021-08-31 at 11.21.59 pm

That's just about everything! The only part we don't have is the buttons.

Wiring up the buttons

To wire up the buttons we'll need to write some JavaScript. First we'll set up some boilerplate for creating the carousel element:

class CarouselElement extends HTMLElement {
  constructor() {
    super();

    this.attachShadow({mode: 'open'});
    const template = document.getElementById('carousel');
    const clone = template.content.cloneNode(true);
    this.shadowRoot.appendChild(clone);
  }
}

customElements.define('ben-carousel', CarouselElement);
Enter fullscreen mode Exit fullscreen mode

Now we want to wire up the events. Because we've already given each of our elements an id this is rather easy. First we get each of the elements we need from the shadowRoot:

// ... inside the constructor

const images = this.shadowRoot.getElementById('images');
const prev = this.shadowRoot.getElementById('prev');
const next = this.shadowRoot.getElementById('next');

Enter fullscreen mode Exit fullscreen mode

Then we set up click handlers that scroll the images container:

// ... inside the constructor

prev.addEventListener('click', () => {
  images.scrollLeft -= 100;
})

nextButton.addEventListener('click', () => {
  images.scrollLeft += 100;
})
Enter fullscreen mode Exit fullscreen mode

And bingo bango we've got a scrolling carousel!

Interested in Web Components?

I'm speaking about Practical Uses for Web Components at Web Directions: Code on September 17 & 24 2021. If you're interested you can use the voucher bensentme to get 20% off!

DElgPan_Fq7SbJfAKW_1Q_L_U2HSzzpnKuwR1onC9M3VGLRykUod7atRXJTePP4wGCk3H995w7W7-BfXrNnw8jL52tDBrrmSASqYKUIT0i0nAWDRo59G0OPiRF_UVEP0A4tVGGxS

Discussion (1)

Collapse
dannyengelman profile image
Danny Engelman • Edited

You can rewrite:

 constructor() {
    super();
    this.attachShadow({mode: 'open'});
    const template = document.getElementById('carousel');
    const clone = template.content.cloneNode(true);
    this.shadowRoot.appendChild(clone);
  }
Enter fullscreen mode Exit fullscreen mode

to:

 constructor() {
    const template = document.getElementById('carousel');
    const clone = template.content.cloneNode(true);
    super()
      .attachShadow({mode: 'open'})
      .append(clone);
  }
Enter fullscreen mode Exit fullscreen mode

Dev.to: 5 more lessons after Web Components 101