Elastic Image Slideshow with Thumbnail Preview

Today we want to show you how to create a simple elastic slideshow with a thumbnail preview. The slideshow will adjust automatically to its surrounding container and we can navigate through the slides by using the thumbnail previewer or the autoplay slideshow option.

Today we want to show you how to create a simple elastic slideshow with a thumbnail preview. The slideshow will adjust automatically to its surrounding container and we can navigate through the slides by using the thumbnail previewer or the autoplay slideshow option.

To make this slideshow responsive, we will use a mixture of JavaScript and CSS techniques.

The fabulous photography used in the demo is by Bartek Lurka and it is licensed under the Attribution-NonCommercial-NoDerivs 3.0 Unported License.

So, let’s do it!

The Markup

We will create two unordered lists, one for the main slider and one for the thumbnail navigation beneath the large image. The “large slider” list elements will contain the image and a title with an h2 and h3 element:

<div id="ei-slider" class="ei-slider">
	<ul class="ei-slider-large">
			<img src="images/large/1.jpg" alt="image01" />
			<div class="ei-title">
	<ul class="ei-slider-thumbs">
		<li class="ei-slider-element">Current</li>
			<a href="#">Slide 1</a>
			<img src="images/thumbs/1.jpg" alt="thumb01" />

The list for the thumbnail preview navigation will contain an absolute element (the first list element with the class ei-slider-element and the thumbnail list elements which consist of an anchor and an image (the thumbnail).

Now, let’s add the style.


First, we will define the style for the main wrapper. We will have the slider inside of a wrapper which will be 100% in width in order to stretch over the whole window. Now, the slider itself will also have a width of 100%, making it use all the space there is in width. But we will also define a maximum width, so that the images in our slider don’t get stretched too much when dealing with a big screen:

	position: relative;
	width: 100%;
	max-width: 1920px;
	height: 400px;
	margin: 0 auto;

While the images are loading, we will add a loading element which will have the following style:

	width: 100%;
	height: 100%;
	position: absolute;
	top: 0px;
	left: 0px;
	background: rgba(0,0,0,0.9);
	color: #fff;
	text-align: center;
	line-height: 400px;

The unordered list will occupy all the space we defined and will not show any overflow:

	height: 100%;
	width: 100%;
	overflow: hidden;

The list elements that will hold the images will be of absolute position. Depending from where we navigate we will slide them from the left or from the right:

.ei-slider-large li{
	position: absolute;
	top: 0px;
	left: 0px;
	overflow: hidden;
	height: 100%;
	width: 100%;

The width of the image will be set in our JavaScript but we also want to define it if we don’t have JS enabled:

.ei-slider-large li img{
	width: 100%;

The title holder will be positioned in the middle of the list element with a right margin to fit the picture in our example (and not to overlap the face in the photography):

	position: absolute;
	right: 50%;
	margin-right: 13%;
	top: 30%;

The style for the headings is the following:

.ei-title h2, .ei-title h3{
	text-align: right;
.ei-title h2{
	font-size: 40px;
	line-height: 50px;
	font-family: 'Playfair Display', serif;
	font-style: italic;
	color: #b5b5b5;
.ei-title h3{
	font-size: 70px;
	line-height: 70px;
	font-family: 'Open Sans Condensed', sans-serif;
	text-transform: uppercase;
	color: #000;

The navigation list will have a small height of 13 pixels. We will set a default width for the thumbnails in the initialisation of our plugin. From that width we will set a max-width to the unordered list. This will make it elastic when we resize the window, but not occupy all the width there is.

	height: 13px;
	margin: 0 auto;
	position: relative;

The list elements of the navigation list will be of relative position:

.ei-slider-thumbs li{
	position: relative;
	float: left;
	height: 100%;

The special slider element that indicates the current image will be positioned absolutely on top of the current thumbnail element:

.ei-slider-thumbs li.ei-slider-element{
	top: 0px;
	left: 0px;
	position: absolute;
	height: 100%;
	z-index: 10;
	text-indent: -9000px;
	background: rgba(0,0,0,0.9);

The link elements will have a white box shadow to show a tiny separation and some darker shadow to appear under them. We’ll also add a transition to the element so that we can change the background-color smoothly on hover:

.ei-slider-thumbs li a{
	display: block;
	text-indent: -9000px;
	background: #666;
	width: 100%;
	height: 100%;
	cursor: pointer;
        0px 1px 1px 0px rgba(0,0,0,0.3), 
        0px 1px 0px 1px rgba(255,255,255,0.5);
    transition: background 0.2s ease;
.ei-slider-thumbs li a:hover{
	background-color: #f0f0f0;

The image will be positioned absolutely and we will add a transition and a box reflection to it. Adding a max-width to it will make sure that the thumb will adjust to the size of the list element when the window gets smaller than the width of the unordered list itself:

.ei-slider-thumbs li img{
	position: absolute;
	bottom: 50px;
	opacity: 0;
	z-index: 999;
	max-width: 100%;
    transition: all 0.4s ease;
        below 0px -webkit-gradient(
            left top, 
            left bottom, 
            color-stop(50%, transparent), 

On hover, we will animate the opacity and the bottom value so that it appears to be sliding in from the top:

.ei-slider-thumbs li:hover img{
	opacity: 1;
	bottom: 13px;

Last but not least, we want to make sure that from a certain width on, the slider title will not cover the best parts of our image. So, we will make it appear at the bottom of the image with a semi-transparent white background:

@media screen and (max-width: 830px) {
		position: absolute;
		right: 0px;
		margin-right: 0px;
		width: 100%;
		text-align: center;
		top: auto;
		bottom: 10px;
		background: rgba(255,255,255,0.9);
		padding: 5px 0;
	.ei-title h2, .ei-title h3{
		text-align: center;
	.ei-title h2{
		font-size: 20px;
		line-height: 24px;
	.ei-title h3{
		font-size: 30px;
		line-height: 40px;

For the case that we don’t have JavaScript enabled we will add this piece of CSS to ensure that our slides are shown. We will hide the thumbnail navigation:

	height: auto;
	display: none;
.ei-slider-large li{
	position: relative;

And that’s all the style! Let’s take a look at the JavaScript.

The JavaScript

Since we are creating a plugin, let’s first look at the definition of the options:

$.Slideshow.defaults 		= {
	// animation types:
	// "sides" : new slides will slide in from left / right
	// "center": new slides will appear in the center
	animation			: 'sides', // sides || center
	// if true the slider will automatically 
	// slide, and it will only stop if the user 
	// clicks on a thumb
	autoplay			: false,
	// interval for the slideshow
	slideshow_interval	: 3000,
	// speed for the sliding animation
	speed			: 800,
	// easing for the sliding animation
	easing			: '',
	// percentage of speed for the titles animation. 
	// Speed will be speed * titlesFactor
	titlesFactor		: 0.60,
	// titles animation speed
	titlespeed			: 800,
	// titles animation easing
	titleeasing			: '',
	// maximum width for the thumbs in pixels
	thumbMaxWidth		: 150

In the _init funtion we will start by setting the opacity of the title elements and the images to 0. We will also preload the images and once they are loaded we will set their size and position according to the slider width and height. Then we configure the thumbnails navigation by setting the width of the unordered list and the list items.

We will then show the first slide and if we set autoplay in our options to true, then we’ll start the slideshow. The we initialize the events which are the events for resizing the window and for clicking the thumbnails:

_init 				: function( options ) {
	this.options 		= $.extend( true, {}, $.Slideshow.defaults, options );
	// set the opacity of the title elements and the image items
	this.$imgItems.css( 'opacity', 0 );
	this.$imgItems.find('div.ei-title > *').css( 'opacity', 0 );
	// index of current visible slider
	this.current		= 0;
	var _self			= this;
	// preload images
	// add loading status
	this.$loading		= $('
').prependTo( _self.$el ); $.when( this._preloadImages() ).done( function() { // hide loading status _self.$loading.hide(); // calculate size and position for each image _self._setImagesSize(); // configure thumbs container _self._initThumbs(); // show first _self.$imgItems.eq( _self.current ).css({ 'opacity' : 1, 'z-index' : 10 }).show().find('div.ei-title > *').css( 'opacity', 1 ); // if autoplay is true if( _self.options.autoplay ) { _self._startSlideshow(); } // initialize the events _self._initEvents(); }); },

And here are the single functions we just talked about:

_preloadImages		: function() {
	// preloads all the large images
	var _self	= this,
		loaded	= 0;
	return $.Deferred(
		function(dfd) {
			_self.$images.each( function( i ) {
				$('').load( function() {
					if( ++loaded === _self.itemsCount ) {
				}).attr( 'src', $(this).attr('src') );
_setImagesSize		: function() {
	// save ei-slider's width
	this.elWidth	= this.$el.width();
	var _self	= this;
	this.$images.each( function( i ) {
		var $img	= $(this);
			imgDim	= _self._getImageDim( $img.attr('src') );
			width		: imgDim.width,
			height		: imgDim.height,
			marginLeft	: imgDim.left,
			marginTop	: imgDim.top

_getImageDim		: function( src ) {
	var $img    = new Image();
	$img.src    = src;
	var c_w		= this.elWidth,
		c_h		= this.$el.height(),
		r_w		= c_h / c_w,
		i_w		= $img.width,
		i_h		= $img.height,
		r_i		= i_h / i_w,
		new_w, new_h, new_left, new_top;
	if( r_w > r_i ) {
		new_h	= c_h;
		new_w	= c_h / r_i;
	else {
		new_h	= c_w * r_i;
		new_w	= c_w;
	return {
		width	: new_w,
		height	: new_h,
		left	: ( c_w - new_w ) / 2,
		top		: ( c_h - new_h ) / 2

_initThumbs			: function() {

	// set the max-width of the slider elements to the one set in the plugin's options
	// also, the width of each slider element will be 100% / total number of elements
		'max-width' : this.options.thumbMaxWidth + 'px',
		'width'		: 100 / this.itemsCount + '%'
	// set the max-width of the slider and show it
	this.$sliderthumbs.css( 'max-width', this.options.thumbMaxWidth * this.itemsCount + 'px' ).show();
_startSlideshow		: function() {

	var _self	= this;
	this.slideshow	= setTimeout( function() {
		var pos;
		( _self.current === _self.itemsCount - 1 ) ? pos = 0 : pos = _self.current + 1;
		_self._slideTo( pos );
		if( _self.options.autoplay ) {
	}, this.options.slideshow_interval);


The _slideTo function will take care of the transition between the slides. Depending on what we’ve set in our options, we’ll either make the new slide appear from the side or simply make it fade in without sliding it. We’ll also take care of the title and its heading elements which we will slightly slide from the sides by setting their right margins. The thumbnail slider element will have to move to the new corresponding thumbnail position.

_slideTo			: function( pos ) {
	// return if clicking the same element or if currently animating
	if( pos === this.current || this.isAnimating )
		return false;
	this.isAnimating	= true;
	var $currentSlide	= this.$imgItems.eq( this.current ),
		$nextSlide		= this.$imgItems.eq( pos ),
		_self			= this,
		preCSS			= {zIndex	: 10},
		animCSS			= {opacity	: 1};
	// new slide will slide in from left or right side
	if( this.options.animation === 'sides' ) {
		preCSS.left		= ( pos > this.current ) ? -1 * this.elWidth : this.elWidth;
		animCSS.left	= 0;
	// titles animation
	$nextSlide.find('div.ei-title > h2')
			  .css( 'margin-right', 50 + 'px' )
			  .delay( this.options.speed * this.options.titlesFactor )
			  .animate({ marginRight : 0 + 'px', opacity : 1 }, this.options.titlespeed, this.options.titleeasing )
			  .find('div.ei-title > h3')
			  .css( 'margin-right', -50 + 'px' )
			  .delay( this.options.speed * this.options.titlesFactor )
			  .animate({ marginRight : 0 + 'px', opacity : 1 }, this.options.titlespeed, this.options.titleeasing )
		// fade out current titles
		$currentSlide.css( 'z-index' , 1 ).find('div.ei-title > *').stop().fadeOut( this.options.speed / 2, function() {
			// reset style
			$(this).show().css( 'opacity', 0 );	
		// animate next slide in
		$nextSlide.css( preCSS ).stop().animate( animCSS, this.options.speed, this.options.easing ),
		// "sliding div" moves to new position
			left	: this.$thumbs.eq( pos ).position().left
		}, this.options.speed )
	).done( function() {
		// reset values
			$currentSlide.css( 'opacity' , 0 ).find('div.ei-title > *').css( 'opacity', 0 );
			$nextSlide.css( 'z-index', 1 );
			_self.current	= pos;
			_self.isAnimating		= false;

The _initEvents function will recalculate the sizes of the images when we resize the window and reposition the thumbnail slider element. When clicking on a thumbnail we will show the regarding slide:

_initEvents			: function() {
	var _self	= this;
	// window resize
	$(window).on( 'smartresize.eislideshow', function( event ) {
		// resize the images
		// reset position of thumbs sliding div
		_self.$sliderElem.css( 'left', _self.$thumbs.eq( _self.current ).position().left );
	// click the thumbs
	this.$thumbs.on( 'click.eislideshow', function( event ) {
		if( _self.options.autoplay ) {
			clearTimeout( _self.slideshow );
			_self.options.autoplay	= false;
		var $thumb	= $(this),
			idx		= $thumb.index() - 1; // exclude sliding div
		_self._slideTo( idx );
		return false;

And that’s it! I hope you enjoyed this tutorial and find it useful!

Tagged with:

Manoela Ilic

Manoela is the main tinkerer at Codrops. With a background in coding and passion for all things design, she creates web experiments and keeps frontend professionals informed about the latest trends.

Stay in the loop: Get your dose of frontend twice a week

πŸ‘Ύ Hey! Looking for the latest in frontend? Twice a week, we'll deliver the freshest frontend news, website inspo, cool code demos, videos and UI animations right to your inbox.

Zero fluff, all quality, to make your Mondays and Thursdays more creative!

Feedback 98

Comments are closed.
  1. How can we use our own buttons to move image left or right or pause? What would be the javascript necessary to do this?

  2. Great job! This is absolutely stunning! I would love to use this on a project I am working on, but I can’t see any license information anywhere. Do you release the tutorial code under any specific license?

  3. great tool for website to be used! I really liked it and its being used at my work

  4. As like everyone else this is awesome work. I am using this in one of my projects and have it working just fine, but the image transitions are “sticky” and don’t flow very well. Any idea what si going on?

    You can view the demo site here

    Thanks for the help!

    • Don’t use “.png” images, but try to use “.jpg” images instead. And good lucky mate πŸ˜‰

    • RJ, seems like you have changed only the file extension. But it isn’t enough. You need to resave it like a “.jpg” image file from any image manipulation program like Photoshop. And then it will work for you, i’m sure πŸ˜‰ p.s.: Unfortunately you’ll need think the background of these “.jpg” images so that it goes with the background image of the page. But i know you have good will. Btw, I liked your project with this resource created by Mary Lou. God bless you. Hope have helped out.

    • You’re also missing thumbnail #3.

      I second the JPG option except save them for web at about 60%, not 100%. PNG files (and high compression JPF files) are HEAVY and slow down code as it’s a lot of data to push. You can’t just re-name files and have them be true to that file format. Need to re-save all.

  5. Hello, Its a Great Slider, But I want to make some changes to it.
    How can i change it to only 4 photos slider, I tried to delete 5,6,7 But the Slider still there for 5,6,7 Images.
    and how can i remove the thumbs for Slider, I don’t want it.

    Thank You

  6. I like this slider but it seems to only be a good fit if all your photos are the landscape view. What if you have a mix of tall and wide photos. How can one modify the CSS to accommodate and how can you get the thumbnail pagination to adjust so that it moves with either a tall or wide photo. thx

  7. Hi there,

    This slider is amazing. I just have a few minor issues and wondered whether someone might help?

    I have NextGen Gallery viewable within each slide. My site also uses Parallax Scrolling, so swapping between slides doesn’t work. Is there a way to do a next and previous button?

    Also, auto play doesn’t work when it comes to switching sides… no idea what this is? πŸ˜€ This is all using WordPresss…

    Thanks for all your help πŸ˜€

  8. Hi, Mary Lou. Nice tut! I love your work and I apreciate if you can help me a little bit here: http://thehitz.com.br/th/discografia.php.
    I can’t make the slider occupy all the width of the screen. With 7 thumbs it was alright but when i changed to 5 images, it dont take all the width.
    What can I do?
    Thank you, very much!

  9. Andone more thing, with 7 images, when the autoplay is on, after the last image shows up, it goes to the first one.
    After i change to 5 images, it stops and don’t make the loop πŸ™

  10. So cool!… this is the best slideshow, I will use it on a project ;)…
    I also like other articles you wrote, very useful, I loved the “content rotator”, the only thing I still can’t get is the validation… because the “data-content” prop. in the thumbs div, I’m starting to love jquery but I didn’t know how to change the property in the js in order to avoid using the thumbs with that property (to validate)…
    Just thank you very much, great job! and greetings from Colombia! πŸ™‚

  11. I’m changing the time interval of the slides, but does not work.

    slideshow_interval : 3000,
    slideshow_interval : 8000,

    This very fast, I need it to be slower. Can anyone help me?


  12. Hi Thanks for such a great stuff.. I want to replace the images with a text contents. when i replace this with text, the page will be stuck with loading. How can i overcome this.

  13. Great galleryscript! I really like it.
    However I would like to make one adjustment to it and that is that the images find their size by the height, not width. So that 100%of the height of the image is always shown, and it adjusts the width accordingly. I tried to alter it myself without any luck. Anyhelp? πŸ™‚

  14. 1920×1080 resolution cuts off the the top portion on the large images, mostly cutting off faces. kinda gnarly, but a needed modification to your almost perfect code.

    The tutorial is magnificent, greatly appreciated. I had been trying to create my own custom control, but on a deadline this is a much needed guide to alter as to accomplish my needs. Thank you!

  15. Also, making the height property of class .ei-slider a percentage value causes the image to disappear.. ?? I need that to adjust with the page, too. Unless i broke something tweaking the code, the re-sizing isn’t working as I need it to. If someone happens to find a work around that allows the height to be adjusted alongside the width in the control so that no matter the platform or device, the slideshow will appear as needed, please post what needs to be adjusted.

  16. Thanks so much for this it’s great! I’m hoping someone might be able to help me with something though. When I resize the window in FF or view it on a mobile device the images resize off the div so you can’t see them. Any idea how to fix it? You can view my site here: http://www.ambushartistics.com/NewSite

  17. Hi,

    First of all, thank you for the slider it’s great!

    I have a question regarding adding content to the slider.
    I was thinking of making it sort of like a tabslider. My question is how do we add a link so that when the user clicks the link it will take him/her to another page.
    Ex. Let’s say I am displaying a product and in we have the title and is a short description then I want to add a button that says learn more. It doesn’t have to be a button it can just be a link.

    How can I do that? is It possible?

  18. Thanks Mary Lou, a great slider and responsive as well. I will be replacing the Wow Slider on this website http://www.colincrawford.co.uk/ as it is not responsive for the Twenty Eleven them in WordPress.

    Can’t see the point using Wow Slider now with a responsive theme and I have asked them could they fix it but had no reply yet. However, I like the titles and the fonts used and it will make the website look more professional.


  19. i have huge problem the slider starts from the center position but showing the first image and its even not completely aligned in the box its in the middle of two boxes and then it goes to the second box and slideshow continues so basically from the start it won’t start from the first box but it will continue to work properly from the second box after that i’m not sure if i am explaining the problem in a good way but i hope you’ll understand something out of it.

  20. While adding images dynamically to this slider, if you are unsure if the image received will be in portrait or landscape format and would rather not turn it on it’s side if it is portrait but still want the image to fit without cutting off any of the image, on the class rule .ei-slider-large li img(width:100%} change width to height and add margin: 0 auto; and the image will display as needed while remaining centered. 0.0 This is a fun code to work with in creating a dynamic module for databound images. The circumstance it is useful for me in is that I need to display query results in a modular template powerful enough to display, load images at appropriate times, and maintain format and data transfer efficiency. So far I haven’t achieved what I hope to, but considering I’ve only been coding fora few months this tutorial is a great help to my cause, allowing me to learn and expand as I go.

    • hi Brett,
      thanks for the tip. i have change the width to height and added the margin, but my portrait pictures are still not showing correct. any clue?


  21. Great script, thanks for sharing. any body know how to enable “PAUSE ON MOUSEOVER” when using the autoplay version of this script? any help greatly appreciated.

  22. Great slider ! I have a question, this slider can work with iframe instead of img ?

  23. That’s really slick. It would be nice if the right and left arrow keys could change the active image, but I’m guessing I can probably hack that in without too much effort.

  24. After a click in de sliding window, I want to load a webpage in an iframe. all reference’s in the ‘a’ tag are not forwarded, how do i do this?

  25. Another cool post. Thanks for sharing so many creative ideas.

    I did the following in Photoshop to help my portrait and landscape images size correctly:

    1. Create new page in Photoshop that is 1280x500px (WidthxHeight) with a white background
    2. Place image at the center of the page and adjust the size if either of the dimensions are greater than 1280 or 500px.
    4. For the large image, save the file as a jpg sized 1280x500px
    3. For the thumbnail, save the same file as a jpg sized 150x59px

  26. I resize the slide to 650 px wide but can you please tell me how to make it indent to the right? it always stays at the center of my page. Thanks

  27. Excellent work!

    Question though, I want to add another line of text below the attribute and name it .

    I added in all the variables properly in the style.css file:

    .ei-title h2, .ei-title h3, .ei-title h4{ text-align: left; } .ei-title h2{ font-size: 50px; line-height: 50px; font-family: 'Helvetica', serif; font-weight: bold; color: #474d51; } .ei-title h3{ font-size: 22px; line-height: 34px; font-family: 'Helvetica', sans-serif; font-weight: normal; color: #474d51; } .ei-title h4{ font-size: 16px; line-height: 24px; font-family: 'Helvetica', sans-serif; color: #474d51; }

    I also added the variable into the .js code:

    // titles animation $nextSlide.find('div.ei-title > h2') .css( 'margin-right', 50 + 'px' ) .stop() .delay( this.options.speed * this.options.titlesFactor ) .animate({ marginRight : 0 + 'px', opacity : 1 }, this.options.titlespeed, this.options.titleeasing ) .end() .find('div.ei-title > h3') .css( 'margin-right', -50 + 'px' ) .stop() .delay( this.options.speed * this.options.titlesFactor ) .animate({ marginRight : 0 + 'px', opacity : 1 }, this.options.titlespeed, this.options.titleeasing ) .find('div.ei-title > h4') .css( 'margin-right', -50 + 'px' ) .stop() .delay( this.options.speed * this.options.titlesFactor ) .animate({ marginRight : 0 + 'px', opacity : 1 }, this.options.titlespeed, this.options.titleeasing )

    When the page loads, the text shows, but once it switches to the next slider and any thereafter on it’s first rotation, the text disappears and once it finishes it’s complete rotation, the first slider (and all thereafter still) text no longer shows, but the and text still do.

    In all instances the text slide-in has ceased to work.

    Any ideas what that all might be about?

    • OK, duhhhh me, fixed the not showing up issue, needed an .end() attribute between and in the jquery.eislideshow.js file.

      however the text sliding in still does not work. πŸ™

      Any ideers?

  28. A beautiful tutorial.

    How do i use this tutorial to work with my WordPress Website.

    Please Help.