logo
post image

Creating a Slider / Carousel with CSS Flexbox (with infinite repeating items in loop)

CSS flex allows to lay elements in a single direction, either horizontally or vertically. Items in a slider / carousal are placed horizontally — going by this condition, CSS flex is a well suited candidate for creating a slider.

Using flexbox to create a slider is not difficult, however the main issue is to repeat items while looping the slider. For example, after the last item we would like the first item to again come in. Or if moving in right direction, after the first item goes out, the last item should come in.

To achieve this requires changing the order property for all flexbox items right after an item finishes its sliding movement (this can be detected using transitionend or animationend Javascript event).

The item that slides inside is set with order: 1. The element that should come in next is set with order: 2 and so on. This needs to be set with Javascript.

This tutorial only explains left-side movement, for code simplicity. Right-side movement can also be achieved with similar logic.

Example

Item 1
Item 2
Item 3
Item 4

HTML, CSS & Javascript

<div id="slider-container-outer">
	<div id="slider-container" class="slider-container-transition">
		<div class="slider-item" data-position="1">Item 1</div>
		<div class="slider-item" data-position="2">Item 2</div>
		<div class="slider-item" data-position="3">Item 3</div>
		<div class="slider-item" data-position="4">Item 4</div>
	</div>
</div>
<button id="move-button">Move Item</button>
#slider-container-outer {
	overflow: hidden;
}

#slider-container {
	display: flex;
	flex-wrap: nowrap;
	flex-direction: row;
}

/* CSS transition applied when translation of items happen */
.slider-container-transition {
	transition: transform 0.7s ease-in-out;
}

.slider-item {
	width: 100%;
	flex-shrink: 0;
}
const FlexSlider = {
	// total no of items
	num_items: document.querySelectorAll(".slider-item").length,
	
	// position of current item in view
	current: 1,

	init: function() {
		// set CSS order of each item initially
		document.querySelectorAll(".slider-item").forEach(function(element, index) {
			element.style.order = index+1;
		});

		this.addEvents();
	},

	addEvents: function() {
		var that = this;

		// click on move item button
		document.querySelector("#move-button").addEventListener('click', () => {
			this.gotoNext();
		});

		// after each item slides in, slider container fires transitionend event
		document.querySelector("#slider-container").addEventListener('transitionend', () => {
			this.changeOrder();
		});
	},

	changeOrder: function() {
		// change current position
		if(this.current == this.num_items)
			this.current = 1;
		else 
			this.current++;

		let order = 1;

		// change order from current position till last
		for(let i=this.current; i<=this.num_items; i++) {
			document.querySelector(".slider-item[data-position='" + i + "']").style.order = order;
			order++;
		}

		// change order from first position till current
		for(let i=1; i<this.current; i++) {
			document.querySelector(".slider-item[data-position='" + i + "']").style.order = order;
			order++;
		}

		// translate back to 0 from -100%
		// we don't need transitionend to fire for this translation, so remove transition CSS
		document.querySelector("#slider-container").classList.remove('slider-container-transition');
		document.querySelector("#slider-container").style.transform = 'translateX(0)';
	},

	gotoNext: function() {
		// translate from 0 to -100% 
		// we need transitionend to fire for this translation, so add transition CSS
		document.querySelector("#slider-container").classList.add('slider-container-transition');
		document.querySelector("#slider-container").style.transform = 'translateX(-100%)';
	}
};

FlexSlider.init();
  • Every time an item needs to come in, the slider container is translated from 0 to -100% (with sliding animation).
  • Every time an item finishes coming in, the CSS order for items needs to be changed. However changing order will change the current displayed item as well. To negate this, slider container is translated back, from -100% to 0 (with no animation).
  • The item that moves in has order as 1. The next item that should come will have order as 2. And so on.