Tutorial: Object-Oriented Way of Creating a jQuery Image Slider
The purpose of this article is to demonstrate object-oriented design in JavaScript, by creating a jQuery powered image gallery script from scratch. Note that we are not creating a jQuery plugin, but simply using jQuery for DOM selection and manipulation. In the near future, this article might be updated to jQuery plugin version.
Resources
Demonstration: http://test.thecodecentral.com/demos/simple_gallery/
See it on GitHub: https://github.com/thecodecentral/Simple-Image-Slider
The Theory
The theory behind it is pretty simple. We first align a list of images side by side, this will allow us to create a long horizontal panel. Above the panel, we create a "windowed" area to show only part of the panel. We achieve this by setting style of the container element to overflow:none. Lastly, we implement JavaScript code to move the horizontal panel.
Creating the Object Blueprint
There's no such thing really is class in JavaScript, so we will call it object blueprint for now. But for convenience, we will loosely refer this as class later on. We are going to create something like this,
var SimpleGallery = function(){
//constructor
};
SimpleGallery.prototype = {
//implementation
};
Once we have the blue print, we can create the gallery object like this,
var gallery = new SimpleGallery();
If you are curious about how it works, here is a good read about applying OOP paradigm in JavaScript.
HTML/CSS Prototype
Before implementing the JavaScript further, we are going to create a quick prototype of the gallery. No JavaScript is involved in this step.
<html>
<head>
</head>
<body>
<div id="gallery" class="simple_gallery" style="display: none">
<div class="slide_panel">
<img src="images/1.jpg" alt="Image 1"/>
<img src="images/2.jpg" alt="Image 2"/>
<img src="images/3.jpg" alt="Image 3"/>
<img src="images/4.jpg" alt="Image 4"/>
<img src="images/5.jpg" alt="Image 5"/>
<img src="images/6.jpg" alt="Image 6"/>
<img src="images/7.jpg" alt="Image 7"/>
</div>
</div>
</body>
</html>
By this little amount of code, we already have a bare-bone version of the gallery we are trying to create. DIV.slide_panel is now scrollable. If you have Firefox browser with Firebug installed, you can scroll it by editing the left position of this DIV element. See a live demonstration here.
We are going to expand our JavaScript code based on this prototype gallery.
Coding the Gallery Script
Firstly, please have a look at the completed script to get an overview of what it looks like. The entire script seems long, but it is divided into many well defined code blocks, which can help you to understand the code better. The constructor now has been updated to initialize many object wide variables. You don't need to know what these are, but just remember they will be used in some places within the class.
//constrcutor
var SimpleGallery = function(options){
this.renderToEl = null;
this.options = $.extend({
slidePanelClassName: 'slide_panel',
navLeftClassName: 'nav_left',
navRightClassName: 'nav_right',
slideClassName: 'slide_block', //class to determine the width of each slide,
itemRenderer: this.itemRenderer,
galleryRenderer: this.galleryRenderer
}, options);
//so on ...
//see simple.gallery.js
};
One thing may be confusing to new jQuery user is the using of $.extend. Here we use $.extend to merge options passed to the constructor with the default options inside of the constructor. For example, if we do
new SimpleGallery({slideClassName: 'slide_new'});
this.options.slideClassName will become 'slide_new', instead of the old value 'slide_block'.
The rest is the class body, which are merely functions.
SimpleGallery.prototype = {
getData:function(){
},
getCurrentSlide:function(){
},
getNumberSlides:function(){
},
getCurrentSlideData:function(){
},
render:function(toEl){
},
scrollTo: function(index){
}
//and so on ...
}
The Scroll to Function
I will focus on discussing the "scrollTo" function, which is the most important function in the class. This function is not hard to understand either. You can almost read it as plain English. The function take a parameter "index", which is the slide number to scroll to. Please read the code comment for more explanations.
/**
* Scroll to a slide. Index is 0 based.
*/
scrollTo: function(index){
//if it is scrolling, do nothing
if(this.isAnimating){
return;
}
//if index number is out of bound, i.e., invalid, do nothing
if(index < 0 || index >= this.numberSlides){
return;
}
//if index is the current slide, do thing
if(index == this.currentSlide){
return;
}
//if we can get to here, scroll to the appropriate slide
this.isAnimating = true;
this.slidePanel.show();
//store "this" context, in order to use it in the animation callback
var self = this;
//hide navigations first, then show after animation is completed.
this.navLeft.hide();
this.navRight.hide();
//finally, scroll to the image panel
this.slidePanel.animate({
left: (-(index * this.slideWidth)) + 'px'
}, 'slow', function(){
//I can't use this.currentSlide to access class variables, because I am inside of a callback function.
//"this" in this scope refers to this.slidePanel
//"self" refers to class instance
//so to access class variable, I use "self", which is defined a few lines above
//we have scrolled to the designated slide, update current slide index
self.currentSlide = index;
//indicate that animation is done
self.isAnimating = false;
//handle how to display backward/forward button.
//For example, at the first slide, backward button is hidden; at last page, forward button is hidden.
self.showHideNav();
//to make this class even more customizable, we trigger a event
//when the animation is completed. you can fire more interest events.
//For example, when at first slide, trigger a 'simple.gallery.at_first_slide' event.
self.renderToEl.trigger('simple.gallery.animation_completed');
});
}
Once the scrollTo function is implemented, we just need to add navigation buttons, then we have an image slider. "setupNav" and "showHideNav" functions will take care adding and displaying the navigation buttons.
The render() Function
The "render()" function is responsible to display the image slider. We are going to go over this function shortly, but now, just have a quick look at the code.
/**
* render gallery to the targeted jQuery element
* @param toEl jQuery element
*/
render:function(toEl){
this.renderToEl = toEl;
this.renderToEl.append(this.options.galleryRenderer.apply(this, [this]));
var panel = toEl.find('.' + this.options.slidePanelClassName);
panel.empty();
for(var i in this.data){
var slide = this.options.itemRenderer.apply(this, [this.data[i]]);
panel.append(slide);
}
this.slidePanel = panel;
this.slideWidth = panel.find('.' + this.options.slideClassName).width();
panel.width(this.numberSlides * this.slideWidth);
panel.show();
this.setupNav();
this.scrollTo(0);
}
It take an argument, which is an jQuery element that you want the slider to be appended into. This function should be pretty easy to understand, except two places:
this.options.galleryRenderer.apply(this, [this]) this.options.itemRenderer.apply(this, [this.data[i]])
Above code is roughly the same as below,
this.options.galleryRenderer(this) this.options.itemRenderer(this.data[i])
Then, you might ask, why would I use "apply" instead of calling "galleryRenderer" and "itemRenderer" directly? To answer this question, we need to understand what "apply" does. If you are not familiar with the scope problem in JavaScript, here is a very good read. So basically "apply" will correct the scope of two renderers functions (see below for explanation of what renderer is). Still remember this.options = $.extend({}, options) in the constructor? After that call, this.options is assigned to an object. So calling this.options.galleryRenderer(this), will be in the scope of "options" object, not the SimpleGallery object; but this is not what we want. We use apply to fix this problem.
Notice that I put two render functions in the this.options variable. This will provide maximum flexibility. This way, either renderer can be overwritten and customized. Please see final demonstration page for how to customize the renderer.
Instantiating the Slider Object (Directly)
Once the slider class is done, we can instantiate it. We can do it programmatically like this,
var galleryData = [
{ url="images/1.jpg", title="Image 1", description="Description of the image 1"},
{ url="images/2.jpg", title="Image 2", description="Description of the image 2"},
{ url="images/3.jpg", title="Image 3", description="Description of the image 3"},
{ url="images/4.jpg", title="Image 4", description="Description of the image 4"},
{ url="images/5.jpg", title="Image 5", description="Description of the image 5"},
{ url="images/6.jpg", title="Image 6", description="Description of the image 6"},
{ url="images/7.jpg", title="Image 7", description="Description of the image 7"}
];
var gallery = new SimpleGallery();
gallery.setData(galleryData).render($('#gallery'));
Instantiating the Slider Object (With a Crawler)
Better yet, we can create a slider based on existing images in the document. I have created a simple image crawler, which can extract image information and construct data for the slider class.
/**
* Select all images, turn them into list of
* @param el jQuery element
**/
var SimpleGalleryCrawlerGeneric = function(el){
this.el = el;
};
SimpleGalleryCrawlerGeneric.prototype = {
/**
* extract images from the given element
* @param el jQuery element
* @return image URLs and other information
*/
getData:function(){
var data = [];
var self = this;
this.el.find('img').each(function(i){
var img = $(this);
var item = {
'url':img.attr('src'),
'title':img.attr('alt'),
'description': img.attr('title')
};
data.push(item);
});
return data;
}
};
Now, let's say the original page contains the following,
<ul id="gallery_data">
<li><img src="images/1.jpg" alt="Image 1" title="Description of the image 1"/></li>
<li><img src="images/2.jpg" alt="Image 2" title="Description of the image 2"/></li>
<li><img src="images/3.jpg" alt="Image 3" title="Description of the image 3"/></li>
<li><img src="images/4.jpg" alt="Image 4" title="Description of the image 4"/></li>
<li><img src="images/5.jpg" alt="Image 5" title="Description of the image 5"/></li>
<li><img src="images/6.jpg" alt="Image 6" title="Description of the image 6"/></li>
<li><img src="images/7.jpg" alt="Image 7" title="Description of the image 7"/></li>
</ul>
The image slider can be created like this,
$('#gallery_data').hide();
var galleryData = new SimpleGalleryCrawlerGeneric($('#gallery_data')).getData();
var gallery = new SimpleGallery();
gallery.setData(galleryData).render($('#gallery'));
This will create a good fall back mechanism. That is, when JavaScript is not supported, original images will be displayed. If JavaScript is supported, slider is displayed. Take a look the demo page, and try to disable JavaScript to see how it falls back nicely.
The Completed Code
JavaScript: http://test.thecodecentral.com/demos/simple_gallery/lib/simple.gallery.js
Slider Demo: http://test.thecodecentral.com/demos/simple_gallery/
Leave a Comment
If you would like to make a comment, please fill out the form below.
To my beloved readers:
Please note that you may freely post comments here, but I will most likely not be able to reply to most them due to my current availability.


http://test.thecodecentral.com/demos/simple_gallery/
for HTML code.
All files can be found at https://github.com/thecodecentral/Simple-Image-Slider. You can get a snapshot as a zip file.