Skip to content
This repository has been archived by the owner on Feb 25, 2018. It is now read-only.

The Code explained

Anselm Hannemann edited this page May 28, 2013 · 4 revisions

This page explains the whole code behind CSS-Slider.

The HTML

<section class="slider-wrapper">
    <ol class="slider-content">
        <li id="slide-1"></li>
        <li id="slide-2"></li>
        <li id="slide-3"></li>
        <li id="slide-4"></li>
    </ol>
    <ol class="slider--bullets">
        <li><a href="#slide-1" class="active"></a></li>
        <li><a href="#slide-2"></a></li>
        <li><a href="#slide-3"></a></li>
        <li><a href="#slide-4"></a></li>
    </ol>
</section>

We have a wrapper element and two lists (first one for the content, second one for the bullet navigation). You can add Text, images and all other custom HTML to the <li>.

The CSS

.slider-content {
  list-style: none;
  padding: 0;
  margin: 0;
  overflow: hidden; /* to keep everything inside the frame */
}

First, we reset our lists to not have paddings and margins or bullets. Then we style our list items. Remember one list item is one screen in our slider. We will now set the basic styles and also set it to opacity: 0 as we don't want all to show up but one.

.slider-content > li {
  position: relative;
  float: left;

  overflow: hidden;
  opacity: 0;

  /* Hardware accelerate the things */
  -webkit-transform: translate3d(0, 0, 0);
     -moz-transform: translate3d(0, 0, 0);
       -o-transform: translate3d(0, 0, 0);
      -ms-transform: translate3d(0, 0, 0);
          transform: translate3d(0, 0, 0);

  /* Transition mode: cover-flow; */
  -webkit-transition: all 1500ms ease-in-out;
     -moz-transition: all 1500ms ease-in-out;
       -o-transition: all 1500ms ease-in-out;
      -ms-transition: all 1500ms ease-in-out;
          transition: all 1500ms ease-in-out;
}

As you can see, I've added two things here. translate3d(0, 0, 0) to hardware accelerate the animation on mobile devices so they will be smooth and I added our actual cover-flow animation. This, of course, is only the timing and animation 'feeling' which is set here.

.slider-content > li:target {
  opacity: 1;
  width: 42rem;
  height: 21rem;
}

.slider-content > li:not(:target) {
  opacity: 0;
  width: 0;
}

This code part treats the items to (not) appear when clicking an anchor with the target to the specific item. By setting the width from 0 to a specific value and the other way round while preserving the height we will get the cool effect that while the chosen element grows, the other one gets smaller which results in the cover-flowish effect. Opacity helps supporting this effect and is needed to hide the element.

.slider--bullets {
  position: absolute;
  z-index: 20;

  width: 4rem;
  height: 1rem;
  left: 47.5%;
  bottom: 1rem;

  -webkit-transform: translateZ(0);
}

.slider--bullets > li {
  display: inline-block;
}

.slider--bullets > li > a {
  margin: auto;
  max-width: none;

  text-decoration: none;
  font-family: Helvetica, Arial, sans-serif;
}

.slider--bullets > li > a:before {
  position: relative;
  z-index: 90;

  display: block;

  content: '\2022';
  font-size: 1.5rem;
  line-height: 1;
  text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.25);

  color: rgba(255, 255, 255, 0.65);

  -webkit-transform: translateZ(0);
}

.slider--bullets > li > a.active:before {
  color: #fff;
}

This is the initial setup for the bullet point navigation of the slider. I add the bullets in the pseudo-element :before with the unicode char of a bullet. It also gets a slightly transparent color because this is the inactive item. Then I add the full white color to the active element. That's it so far for the logic of the slider. Let's add some content to it:

/* This is finally adds sample content to slider */

#slide-1,
#slide-2,
#slide-3,
#slide-4 {
  background-size: cover;
}

#slide-1 {
  background-image: url('http://open.anselm-hannemann.com/cover-flow/img/slide-1.jpg');
}

I've set the images to cover the area. This for me is the nicest solution but it's up to you and your use-case which effect to set here as background-size. The actual slide's image is added through CSS although you could do this in HTML. This fully depends on what you want to achieve semantically.

The JavaScript

While I tried to build it only with CSS, this functionality adds bullet highlighting and better initialization.

A note here: I first built the JavaScript using jQuery for all the selector stuff etc. But I wanted to know how easy it is to refactor it in plain JavaScript. It was easier than I thought, here is the result. Be sure to check my blog for an upcoming post on that topic.

The code is in its own namespace object:

var cssSlider = {
	// everything in here
};

and initialized at the bottom:

// Initialize cssSlider
cssSlider.init();

All selectors are now written in the following object:

// CSS-Slider selectors
selectors: {
	container: 'slider-wrapper',
	sliderItems: '.slider-content > li',
	firstSlide: '.slider-content > li:first-child',
	activeSlide: '.slider-content > .active',
	clickFrame: '.slider-clickframe',
	activeBullet: '.slider--bullets .active',
	allBulletLinks: 'a[href^="#slide-"]',
},

It has several components which I will now explain in detail:

sliderRemoveFirstClass: function() {
	var hash = window.location.hash;

	if(hash != "" && hash !== '#slide-1') {
		document.querySelector(cssSlider.selectors.firstSlide).removeAttribute('class');
	}
}

This code snippet removes the initially set .active-class in the markup which is set to prevent flickering on page load.

We now want the user to be able to click the whole slider area to go to the next slide. This is done by that lines of code:

setClickFrame: function() {

	var getContainer = document.getElementsByClassName(cssSlider.selectors.container);
	var newNode = document.createElement("a");

	var hash = window.location.hash;
	var getHashId = 1; // Set initial value to 1 as this the 1st is always loaded

	if(hash !== '') {
		getHashId = hash.match(/\d+(\.\d+)?/g);
		getHashId = parseInt(getHashId[0]);
	}

	var maxCount = document.querySelectorAll(cssSlider.selectors.sliderItems).length;
	var nextItem;

	if(getHashId < maxCount) {
		nextItem = getHashId+1;
	} else {
		nextItem = 1;
	}

	newNode.setAttribute('class', 'slider-clickframe');
	newNode.setAttribute('href', '#slide-'+nextItem);

	getContainer[0].appendChild(newNode);
},

changeClickFrame: function() {

	var getClickFrame = document.querySelector(cssSlider.selectors.clickFrame);
	var hash = window.location.hash;
	var getHashId = hash.match(/\d+(\.\d+)?/g);
	var maxCount = document.querySelectorAll(cssSlider.selectors.sliderItems).length;
	var nextItem;

	getHashId = parseInt(getHashId[0]);

	if(getHashId < maxCount) {
		nextItem = getHashId+1;
	} else {
		nextItem = 1;
	}

	getClickFrame.setAttribute('href', '#slide-'+nextItem);
}

We now have created our new element. It is created when the page loads and attributes are changed when the user changed the slide.

Next step is to set our active items:

setActiveItems: function() {

	var hash = window.location.hash;
	// Set active Slide
	var getActiveSlide = document.querySelector(cssSlider.selectors.activeSlide);

	if (getActiveSlide !== null) {
		getActiveSlide.removeAttribute('class');
	}

	// Set active Bullet
	var activeSlideBullet = document.querySelector(cssSlider.selectors.activeBullet);
	var sliderBulletArray = document.querySelectorAll(cssSlider.selectors.allBulletLinks);

	// Remove old active Class
	activeSlideBullet.removeAttribute('class');

	// Set new active Class
	document.querySelector('a[href="'+hash+'"]').setAttribute('class', 'active');
}

This snippet now sets an .active class to the current slide and bullet so they can be styled individually in CSS.

A few final steps to get the whole thing running smooth are now added:

init: function() {
	var hash = window.location.hash;

	// Set clickable frame on Slider
	window.addEventListener('DOMContentLoaded', cssSlider.setClickFrame);
	window.addEventListener('hashchange', cssSlider.changeClickFrame);

	// Set active item
	window.addEventListener('hashchange', cssSlider.setActiveItems);

	if(hash.indexOf("slide-") >= 0) {
		window.addEventListener('DOMContentLoaded', cssSlider.setActiveItems);
	}

}

Notes

While I do think this is a neat slider, I believe there is still space for improvement. For example there might be a way to get rid of the initial JS hashchange and maybe there is a better solution in JavaScript to set the active-classes for the bullets.

Anyway, it shows you how to easily achieve a pretty graphical effect just with CSS. You don't need any line of jQuery in here.

If you find bugs or have ideas for features or better code, please read the Contribution guidelines and file an issue.

If you have comments, please ping me on twitter or app.net. I am happy to discuss it with you!

Clone this wiki locally