Front-End Code Reusability with CSS and JavaScript

Most web standards-based developers are more than familiar with creating their sites with semantic HTML with lots and lots of CSS. With each new page in a design, the CSS tends to grow and grow and more elements and styles are added. But CSS can be used to better effect.

The idea of object-oriented CSS isn’t new. Nicole Sullivan has written a presentation on the subject and outlines two main concepts: separate structure and visual design; and separate container and content. Jeff Croft talks about Applying OOP Concepts to CSS:

I can make a class of .box that defines some basic layout structure, and another class of .rounded that provides rounded corners, and classes of .wide and .narrow that define some widths, and then easily create boxes of varying widths and styles by assigning multiple classes to an element, without having to duplicate code in my CSS.

This concept helps reduce CSS file size, allows for great flexibility, rapid building of similar content areas and means greater consistency throughout the entire design. You can also take this concept one step further and apply it to site behaviour with JavaScript.

Build a versatile slideshow

I will show you how to build multiple slideshows using jQuery, allowing varying levels of functionality which you may find on one site design. The code will be flexible enough to allow you to add previous/next links, image pagination and the ability to change the animation type. More importantly, it will allow you to apply any combination of these features.

Image galleries are simply a list of images, so the obvious choice of marking the content up is to use a <ul>. Many designs, however, do not cater to non-JavaScript versions of the website, and thus don’t take in to account large multiple images. You could also simply hide all the other images in the list, apart from the first image. This method can waste bandwidth because the other images might be downloaded when they are never going to be seen.

Taking this second concept — only showing one image — the only code you need to start your slideshow is an <img> tag. The other images can be loaded dynamically via either a per-page JavaScript array or via AJAX.

The slideshow concept is built upon the very versatile Cycle jQuery Plugin and is structured in to another reusable jQuery plugin. Below is the HTML and JavaScript snippet needed to run every different type of slideshow I have mentioned above.

<img src="path/to/image.jpg" alt="About the image" title="" height="250" width="400" class="slideshow">
<script type="text/javascript">
	jQuery().ready(function($) {
		$('img.slideshow').slideShow({
			images: ['1.jpg', '2.jpg', '3.jpg']
		});
	});
</script>

Slideshow plugin

If you’re not familiar with jQuery or how to write and author your own plugin there are plenty of articles to help you out.

jQuery has a chainable interface and this is something your plugin must implement. This is easy to achieve, so your plugin simply returns the collection it is using:

return this.each(
	function () {}
};

Local Variables

To keep the JavaScript clean and avoid any conflicts, you must set up any variables which are local to the plugin and should be used on each collection item. Defining all your variables at the top under one statement makes adding more and finding which variables are used easier. For other tips, conventions and improvements check out JSLint, the “JavaScript Code Quality Tool”.

var $$, $div, $images, $arrows, $pager,
	id, selector, path, o, options,
	height, width,
	list = [], li = 0,
	parts = [], pi = 0,
	arrows = ['Previous', 'Next'];

Cache jQuery Objects

It is good practice to cache any calls made to jQuery. This reduces wasted DOM calls, can improve the speed of your JavaScript code and makes code more reusable.

The following code snippet caches the current selected DOM element as a jQuery object using the variable name $$. Secondly, the plugin makes its settings available to the Metadata plugin which is best practice within jQuery plugins.

For each slideshow the plugin generates a <div> with a class of slideshow and a unique id. This is used to wrap the slideshow images, pagination and controls.

The base path which is used for all the images in the slideshow is calculated based on the existing image which appears on the page. For example, if the path to the image on the page was /img/flowers/1.jpg the plugin would use the path /img/flowers/ to load the other images.

$$ = $(this);
o = $.metadata ? $.extend({}, settings, $$.metadata()) : settings;
id = 'slideshow-' + (i++ + 1);
$div = $('<div />').addClass('slideshow').attr('id', id);
selector = '#' + id + ' ';
path = $$.attr('src').replace(/[0-9]\.jpg/g, '');
options = {};
height = $$.height();
width = $$.width();

Note: the plugin uses conventions such as folder structure and numeric filenames. These conventions help with the reusable aspect of plugins and best practices.

Build the Images

The cycle plugin uses a list of images to create the slideshow. Because we chose to start with one image we must now build the list programmatically. This is a case of looping through the images which were added via the plugin options, building the appropriate HTML and appending the resulting <ul> to the DOM.

$.each(o.images, function () {
	list[li++] = '<li>';
	list[li++] = '<img src="' + path + this + '" height="' + height + '" width="' + width + '">';
	list[li++] = '</li>';
});
$images = $('<ul />').addClass('cycle-images');
$images.append(list.join('')).appendTo($div);

Although jQuery provides the append method it is much faster to create one really long string and append it to the DOM at the end.

Update the Options

Here are some of the options we’re making available by simply adding classes to the <img>. You can change the slideshow effect from the default fade to the sliding effect. By adding the class of stopped the slideshow will not auto-play and must be controlled via pagination or previous and next links.

// different effect
if ($$.is('.slide')) {
	options.fx = 'scrollHorz';
}
// don't move by default
if ($$.is('.stopped')) {
	options.timeout = 0;
}

If you are using the same set of images throughout a website you may wish to start on a different image on each page or section. This can be easily achieved by simply adding the appropriate starting class to the <img>.

// based on the class name on the image
if ($$.is('[class*=start-]')) {
	options.startingSlide = parseInt($$.attr('class').replace(/.*start-([0-9]+).*/g, "$1"), 10) - 1;
}

For example:

<img src="/img/slideshow/3.jpg" alt="About the image" title="" height="250" width="400" class="slideshow start-3">

By default, and without JavaScript, the third image in this slideshow is shown. When the JavaScript is applied to the page the slideshow must know to start from the correct place, this is why the start class is required.

You could capture the default image name and parse it to get the position, but only the default image needs to be numeric to work with this plugin (and could easily be changed in future). Therefore, this extra specifically defined option means the plugin is more tolerant.

Previous/Next Links

A common feature of slideshows is previous and next links enabling the user to manually progress the images. The Cycle plugin supports this functionality, but you must generate the markup yourself. Most people add these directly in the HTML but normally only support their behaviour when JavaScript is enabled. This goes against progressive enhancement. To keep with the best practice progress enhancement method the previous/next links should be generated with JavaScript.

The follow snippet checks whether the slideshow requires the previous/next links, via the arrows class. It restricts the Cycle plugin to the specific slideshow using the selector we created at the top of the plugin. This means multiple slideshows can run on one page without conflicting each other.

The code creates a <ul> using the arrows array we defined at the top of the plugin. It also adds a class to the slideshow container, meaning you can style different combinations of options in your CSS.

// create the arrows
if ($$.is('.arrows') && list.length > 1) {
	options.next = selector + '.next';
	options.prev = selector + '.previous';
	$arrows = $('<ul />').addClass('cycle-arrows');
	$.each(arrows, function (i, val) {
		parts[pi++] = '<li class="' + val.toLowerCase() + '">';
		parts[pi++] = '<a href="#' + val.toLowerCase() + '">';
		parts[pi++] = '<span>' + val + '</span>';
		parts[pi++] = '</a>';
		parts[pi++] = '</li>';
	});
	$arrows.append(parts.join('')).appendTo($div);
	$div.addClass('has-cycle-arrows');
}

The arrow array could be placed inside the plugin settings to allow for localisation.

Pagination

The Cycle plugin creates its own HTML for the pagination of the slideshow. All our plugin needs to do is create the list and selector to use. This snippet creates the pagination container and appends it to our specific slideshow container. It sets the Cycle plugin pager option, restricting it to the specific slideshow using the selector we created at the top of the plugin. Like the previous/next links, a class is added to the slideshow container allowing you to style the slideshow itself differently.

// create the clickable pagination
if ($$.is('.pagination') && list.length > 1) {
	options.pager = selector + '.cycle-pagination';
	$pager = $('<ul />').addClass('cycle-pagination');
	$pager.appendTo($div);
	$div.addClass('has-cycle-pagination');
}

Note: the Cycle plugin creates a <ul> with anchors listed directly inside without the surrounding <li>. Unfortunately this is invalid markup but the code still works.

Demos

Well, that describes all the ins-and-outs of the plugin, but demos make it easier to understand! Viewing the source on the demo page shows some of the combinations you can create with a simple <img>, a few classes and some thought-out JavaScript.

View the demos →

Decide on defaults

The slideshow plugin uses the exact same settings as the Cycle plugin, but some are explicitly set within the slideshow plugin when using the classes you have set.

When deciding on what functionality is going to be controlled via this class method, be careful to choose your defaults wisely. If all slideshows should auto-play, don’t make this an option — make the option to stop the auto-play. Similarly, if every slideshow should have previous/next functionality make this the default and expose the ability to remove them with a class such as “no-pagination”.

In the examples presented on this article I have used a class on each <img>. You can easily change this to anything you want and simply apply the plugin based on the jQuery selector required.

Grab your images

If you are using AJAX to load in your images, you can speed up development by deciding on and keeping to a folder structure and naming convention. There are two methods: basing the image path based on the current URL; or based on the src of the image. The first allows a different slideshow on each page, but in many instances a site will have a couple of sets of images and therefore the second method is probably preferred.

Metadata

A method which allows you to directly modify settings in certain plugins, which also uses the classes from your HTML already exists. This is a jQuery plugin called Metadata. This method allows for finer control over the plugin settings themselves. Some people, however, may dislike the syntax and prefer using normal classes, like above which when sprinkled with a bit more JavaScript allows you to control what you need to control.

The takeaway

Hopefully you have understood not only what goes in to a basic jQuery plugin but also learnt a new and powerful idea which you can apply to other areas of your website.

The idea can also be applied to other common interfaces such as lightboxes or mapping services such as Google Maps — for example creating markers based on a list of places, each with different pin icons based the anchor class.

About the author

Trevor Morris a twenty-something web developer based in the Midlands, UK. He is fluent in both front- and back-end coding, and develops usable and accessible front-end interfaces using web standards.

He very occasionally writes on his personal site at trovster.com but for more up to date commentary you can follow him on Twitter. He is also a prominent member of the The Multipack, a community of multi-talented Web professionals from across the West Midlands.

More articles by Trevor

Comments