Video icon 64
Learning to code? Skill up faster with our practical video courses. Start your free trial today.
Advertisement

Create a Spectacular Photo Gallery with MooTools

by

JavaScript libraries, such as MooTools, can save a lot of time when developing a site. In this tutorial, I'll be showing you how to combine many of MooTools's functions to create a nice photo gallery.

Step 1 - Setup Your HTML, CSS, and MooTools

Before starting, take a look at the demo. It will be easier to understand the purpose of each step if you know the goal.

First, create a simple HTML file, and add a link to the stylesheet (main.css) and to two JavaScript files (mootools.js and main.js). Create those files in the same directory. Then go to the MooTools Core Builder. Select all of the components and then click Download. This will download mootools-1.2-core.js. Copy the contents of that file to the mootools.js file. Then got to the MooTools More Builder and select all of the components and click Download. Copy the contents again and paste them at the bottom of mootools.js.

(Note: Most sites, including this photo gallery, do not need all of the components. However, using all of them at the beginning, and then cutting out the ones you don't need make development a lot easier. If you were in the middle of a project and realized you wanted a different featured included in one of the components you didn't download, you would have to download it again before continuing. This way, you only have to download it again after you are finished.)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Photo Gallery</title>

<link rel="stylesheet" type="text/css" href="main.css" />

<script type="text/javascript" src="mootools.js"></script>
<script type="text/javascript" src="main.js"></script>

</head>

<body>

</body>
</html>

Step 2 - Create the Layout

Now we have to create the layout for our photo gallery using HTML and CSS. Add the following code inside the body tags of your HTML file.

<div id="container">

<h1>Photo Gallery</h1>

<div id="picture_section">
	<div id="inside">
		<img src="#" /><img src="#" /><img src="#" /><img src="#" /><img src="#" /><img src="#" />
		<img src="#" /><img src="#" /><img src="#" /><img src="#" /><img src="#" /><img src="#" />
		<img src="#" /><img src="#" /><img src="#" /><img src="#" /><img src="#" /><img src="#" />
		<img src="#" /><img src="#" /><img src="#" /><img src="#" /><img src="#" /><img src="#" />
		<img src="#" /><img src="#" /><img src="#" /><img src="#" /><img src="#" /><img src="#" />
	</div>
</div>

<div id="controls_vert">
	<div id="up" class="deactivated">Scroll Up</div>
    <div id="down">Scroll Down</div>
</div>

<div id="controls_horz">
	<div id="left">Left</div>
    <div id="right">Right</div>
</div>

</div>

This creates a container to surround all of the code in the site, a title, a section for pictures, and controls for the site. Why all of the empty img tags? In the CSS below, we give them sizes and a different background color, so we can test the gallery without having to add and resize actual pictures. Now we add the CSS to the main.css file.

* { margin:0; padding:0; outline:none; }

.hide { display:none; }

body {
	background:#000;
	color:#fff;
	padding:30px;
	font-family:Arial, Helvetica, sans-serif;
}

#container {
	width:596px;
	height:400px;
	background:#111;
	overflow:hidden;
	border:1px solid #333;
}

h1 {
	background:#222;
	width:592px;
	height:29px;
	padding-left:7px;
	border-bottom:1px solid #333;
	font-size:18px;
	line-height:29px;
	font-weight:normal;
}

#picture_section {
	padding:7px 0 7px 7px;
	width:590px;
	height:332px;
	overflow:hidden;
}
	#inside {
		width:590px;
		height:332px;
	}
	#picture_section img {
		border:0;
		height:57px;
		width:111px;
		float:left;
		background:#333;
		margin-bottom:7px;
		margin-right:7px;
		cursor:pointer;
	}

#controls_vert {
	background:#222;
	width:600px;
	height:23px;
	border-top:1px solid #333;
}
#controls_horz {
	background:#222;
	width:600px;
	height:23px;
	border-top:1px solid #333;
}
	#up {
		height:10px;
		width:10px;
		margin-right:7px;
		background:url(up.jpg) no-repeat;
		text-indent:-9999px;
		float:left;
		margin:7px;
		margin-bottom:6px;
		cursor:pointer;
	}

	#down {
		height:10px;
		width:10px;
		background:url(down.jpg) no-repeat;
		text-indent:-9999px;
		float:left;
		margin:7px;
		margin-left:0;
		margin-bottom:6px;
		cursor:pointer;
	}
	#left {
		height:10px;
		width:10px;
		background:url(left.jpg) no-repeat;
		text-indent:-9999px;
		float:left;
		margin:7px;
		margin-bottom:6px;
		cursor:pointer;
	}
	#right {
		height:10px;
		width:10px;
		background:url(right.jpg) no-repeat;
		text-indent:-9999px;
		float:left;
		margin:7px;
		margin-left:0;
		margin-bottom:6px;
		cursor:pointer;
	}

	div#up.deactivated { opacity:0.2; filter:alpha(opacity=20); cursor:default; }
	div#down.deactivated { opacity:0.2; filter:alpha(opacity=20); cursor:default; }
	div#right.deactivated { opacity:0.2; filter:alpha(opacity=20); cursor:default; }
	div#left.deactivated { opacity:0.2; filter:alpha(opacity=20); cursor:default; }

Here is what it looks like with that applied.

Because the gallery starts at the top, the up arrow is automatically disabled. Also, the left and right arrows are automatically hidden, because they will only be displayed when the large image is blown up. Also, only the tops of the images in the sixth row are shown, to let the user know that there are more below.

Now, let's add the overlay that will show the large images. First, add the following code right before the closing body tag in your HTML file.

<div id="display_picture_container">    
<div id="display_picture">Click on the image to go back to the gallery.</div>    
<div id="display_picture_img"></div></div>a

Add the following to the end of your CSS file to make the divs appear as an overlay over the thumbnails.

#display_picture_container {
	position:absolute;
	top:0;
	left:0;
	width:700px;
	height:400px;
	padding-top:16px;
}

#display_picture {
	position:absolute;
	top:61px;
	left:31px;
	width:596px;
	height:330px;
	background:#000;
	opacity:0.8; filter:alpha(opacity=80);
	text-align:center;
	font-size:11px;
	padding-top:16px;
}

#display_picture_img {
	position:absolute;
	top:108px;
	left:65px;
	height:272px;
	width:530px;
	cursor:pointer;
}

That creates this effect:

The large image would appear where the (now invisible) display_picture_img div is (under the Click on the image to go back to the gallery text).

Step 3 - Collect and Resize Your Pictures

This is a good time to collect all of your pictures and resize them. First of all, create a "pictures" folder and a "thumbs" folder. Add all of your pictures to the pictures folder and name them 1.jpg, 2.jpg, 3.jpg, etc. Resize them all to the size of the display_picture_img div: 530 pixels wide and 272 pixels tall. Then copy those files to the thumbs directory and resize those copies to 111 pixels wide by 57 pixels tall. It doesn't matter how many pictures you have. However, I would recommend using over 26 for this tutorial so you can use the vertical scrolling.

Step 4 - Add Functionality With MooTools

The first thing to do is remove all of the <img src="#" /> tags in the HTML file. Those were only placeholders; we will add the actual files with JavaScript later. Now we will make the overlay vanish when the page loads. Add the following code to the main.js file

var number_of_pictures = 32; 

function show_pictures () {
	var while_pictures = 1;
	while(while_pictures <= number_of_pictures) {
		var new_image = new Element('img', {
			'src': 'thumbs/' + while_pictures + '.jpg',
			'id': 'image_' + while_pictures,
			'events': {
				'click': function(){
					$('display_picture_img').innerHTML = "<img src=\"" + this.src.replace('thumbs/', 'pictures/') + "\" id=\"big_picture\" class=\"" + this.id + "\" />";
					$('display_picture_container').fade(1);
					$('big_picture').fade(.999999);
					$('controls_vert').setStyle('display', 'none');

					if(this.id.replace('image_', '')==1) {
						$('left').set('class', 'deactivated');
						$('right').erase('class');
					} else if(this.id.replace('image_', '')==number_of_pictures) {
						$('left').erase('class');
						$('right').set('class', 'deactivated');
					} else {
						$('left').set('class', 'activated');
						$('right').erase('class');
					}

					$('controls_horz').setStyle('display', 'block');
					$('left').tween('margin-left', '286px');
				}
			}
		});

		new_image.inject($('inside'));

		// preload all of the images
		var preload_image = new Element('img', {
			'src': 'pictures/' + while_pictures + '.jpg',
			'class': 'hide'

		});
		preload_image.inject($('container'));

		// NOTE: I didn't create an alt attribute because it won't be seen by anyone here anyway.
		while_pictures++;
	}

}
window.addEvent('domready', function() {	

	show_pictures();

	$('display_picture_container').fade('hide');

});

Here is what it looks like:

To explain this, let's start at the bottom. In MooTools, the window.addEvent code executes the code inside when the DOM is ready to render elements. The $('display_picture_container').fade('hide'); hides the display_picture_container div and all of its contents. I used the fade('hide') method because we will be fading it in later. We don't create a display:none; statement in our CSS file or have JavaScript hide it in that way; it has to be hidden by the function we are going to use to show it again later (fade).

Now for the large part: the show_pictures function. First of all, we set the number_of_pictures variable. This tells the function to add thumbnails from the thumbs folder using every integer from 1 to the given number. To add more pictures to this slideshow, all you have to do is add them to the folders, resize them, and increase the number_of_pictures value. I used 32.

The next part is the internal workings of show_pictures. This does, as the name suggests, show all of the pictures. It also preloads the larger pictures in the background. Inside of the function, while_pictures is defined as 1. This is the variable we will use as the starting point for displaying all of the pictures. The following while statement shows that the code inside will be executed one time for each picture, starting at one and going up to the total number of pictures (number_of_pictures). Notice that while_pictures++; is at the end of the while loop, to have it increase by one each time the loop repeats.

Next, we create an Element instance for an img element. We can add src, id, class, event, and other attributes. However, we only need src, id, and events. By using 'src': 'thumbs/' + while_pictures + '.jpg', we state that we want the src of the image to be whatever oru current number is, and inside the thumbs folder. The ids for all of our thumbnails will be image_1, image_2, etc.

Now we want to have a large image appear after clicking on a thumbnail, so we create a click event. The first line under that makes the display_picture_img div contian the large picture with an id of big_picture and a class of the id of the thumbnail (image_1, image_2, etc.). Remember that, at this time, the display_picture_img div is hidden.

Now use the .fade(1) method to fade in the display_picture_container div and all of its contents. The 1 represents the level of opacity (1 is 100%, .5 is 50%, etc.). However, this causes a bug in all but the newest browsers such as Firefox 3 and Safari. They fade in correctly, but the others just pop in the image and fade the darker area around it. After much experimentation, I found out that if you have the large picture (which has big_picture as its id) fade in with its own fade method, it works—but only if the fade is not set to 1. However, we want the picture to be at 100% opacity. Therefore, I set it to .999999, which has no noticeable transparency.

So now the large picture is on the screen. However, we also want the controls to move to the center and become left and right arrows, instead of up and down arrows. First of all, we hide the vertical controls (controls_vert). We can use display:none; for this, because we are not going to be fading it in or out. Then we use an if, if else, and else statement to determine if the image that was clicked on was the first or last one in our gallery. If it was the first one, we don't want it to be able to go to the left, because no previous one exists. The same thing is necessary at the right. Later on, when we have the left and right arrows working, we will have it detect whether or not it has a deactivated class. This will determine if the click event will work for it. (Also, check out the CSS. It sets the arrow image to 20%. This saves the need of having 8 directional arrow images instead of 4.)

Next, we display the horizontal controls by setting its display style to block. So, by hiding the vertical controls and showing the horizontal controls, we have just switched them out. However, we want the horizontal controls to be centered. I accomplished this by creating a tween that animates an increase in the left margin to 286px, which is the center of the gallery.

Now that we have our new_image created, we have to put it inside of the document. The inject method allows us to insert the newly made img element into the inside div.

We also want to preload each of the images, too, so there won't be any delay when people actually click on the images to fade them in. We create a new img element for each picture that is full-sized. However, it uses the hide class, which, in our CSS, uses display:none; to hide the contents. Then we inject the hidden preloader image into the container div.

Here is what it looks like after you click on a thumbnail:

Now we have to make the picture disappear when we click on it. Insert the following code after the $('display_picture_container').fade('hide'); statement in main.js.

$('display_picture_img').addEvent('click', function(){
		$('display_picture_container').fade(0);
		$('big_picture').fade(0);
		$('up').setStyle('margin-left', '286px');
		$('controls_horz').setStyle('display', 'none');
		$('controls_vert').setStyle('display', 'block');
		$('left').setStyle('margin-left', '7px');
		$('up').tween('margin-left', '7px');
	});

This adds a click event to the div containing the large image. When it is clicked, the container fades out to nothing, and, due to the aforementioned bug, the picture itself is faded to 0 too. Then we set the hidden up div to have the same higher margin-left as the left div has. Then we switch the horizontal controls back to the vertical controls instantaneously, and then, after setting the hidden left div back to its original left margin, we animate the up arrow's left margin back to its original 7px using another tween. Now you can click on thumbnails to blow them up and then click on the large image to hide it again. Almost done!

Now take the following code and paste it above the cod you just entered.

var vertical_moves = 0;
var rows = Math.ceil(number_of_pictures/5);

if(rows>5) {

	$('up').addEvent('click', function(event){
		if(!$('up').hasClass('deactivated')) {
			vertical_moves--;
			$('down').erase('class');
			$('inside').tween('margin-top', '-'+ (64 * vertical_moves) +'px');
			if (vertical_moves==0) {
				$('up').set('class', 'deactivated');
			}
		}
	});

	$('down').addEvent('click', function(event){
		if(!$('down').hasClass('deactivated')) {
			vertical_moves++;
			$('up').erase('class');
			$('inside').tween('margin-top', '-'+ (64 * vertical_moves) +'px');
			if(vertical_moves == (rows-5)) {
				$('down').set('class', 'deactivated');
			}
		}
	});
} else {
	$('up').set('class', 'deactivated');
	$('down').set('class', 'deactivated');
}

var current_id = 1;

$('left').addEvent('click', function(){
	if(!$('left').hasClass('deactivated')) {
		current_id = $('big_picture').get('class').replace('image_', '');
		current_id--;
		$('big_picture').fade('hide');
		$('big_picture').set('src', 'pictures/' + current_id + '.jpg');
		$('big_picture').fade(1);
		$('big_picture').set('class', 'image_' + current_id);
		if(current_id==1) { $('left').set('class', 'deactivated'); }
		if(current_id==(number_of_pictures-1)) { $('right').erase('class');  }
	}
});

$('right').addEvent('click', function(){
	if(!$('right').hasClass('deactivated')) {
		current_id = $('big_picture').get('class').replace('image_', '');
		current_id++;
		$('big_picture').fade('hide');
		$('big_picture').set('src', 'pictures/' + current_id + '.jpg');
		$('big_picture').fade(1);
		$('big_picture').set('class', 'image_' + current_id);
		if(current_id==2) { $('left').erase('class'); }
		if(current_id==number_of_pictures) { $('right').set('class', 'deactivated'); }
	}
});

The first thing we'll do is set up vertical scrolling. First of all, we will create a variable called vertical_moves with a value of 0. This will tell us how many rows it has moved down. Then we find out how many rows of images we have, with 5 images in a row. The Math.ceil function rounds up any number that has a decimal to an integer. For example, I have 32 images. 32 divided by 5 is 6.4, which would round to 6. However, we still want the extra pictures to be shown; even though there are only two pictures on the last row, we want it to count as a whole row. Since Math.ceil rounds it all up, the amount of rows becomes 7.

Next, we detect if there are more than five rows. If there aren't, we deactivate the up and down arrows. With five rows or less, all of the pictures are shown without the need for the scroll buttons. However, if there are six or more, we want to add events to them. For the up arrow div, we add an event and then detect whether it has been labeled as deactivated or not. If it is deactivated, it will show up with only 20% opacity, and when you click on it, it won't do anything. Additionally, it won't have a pointer as a cursor anymore. However, if it doesn't have that class, it continues. If you are going up, it decreases the amount of rows down you have gone, so vertical_moves decreases by one row. It then erases any class that the down div has. If it is on the bottom row and the down arrow is deactivated, once it moves up a row, it will be able to move down again. Therefore, it stops it from being deactivated.

Next, it makes all of the thumbnails move up by detecting what the new vertical_moves is, multiplying it by 64, and making it negative. The number 64 is used because that is the height of a row of thumbnails plus the margin below it. It then applies this value to the margin-top. For example, if the margin-top was originally -128px, it would move upwards because the new margin-top would only be -64px. The tween method makes it fade to its new position. Inside the main.css file, it has the picture_section div hide its overflow. This is so that when you apply a negative margin-top, it hides it instead of sticking through.Then it determines if it is at the top of the gallery. If it is, it deactivates the up arrow so it can't go up any farther. The down arrow has the same actions applied, except in reverse.

Now we will make the left and right buttons function correctly. First of all, we set current_id equal to one to create the variable. This will help us determine which large image is being displayed (remember, the left and right buttons only appear when a large image is being displayed). Then we add a click event to the left div and check if it is deactivated or not. If it isn't, we detect the class of the img with the id of big_picture. This was set earlier as image_(enter number of current image here). We use the get method to find this and the replace method to remove the image_ prefix. Then we subtract it by one, because, by moving to the left, we are going back to a picture with an id of one less.

Next, we hide the image itself instantly, and then change it's src to the previous image. We then fade the image in to 100% opacity. After that, we change the image's class to its new value, which enables a user to go left repeatedly. We then detect if it is at the very first picture. If so, we can't go left anymore, so we deactivate the left button. If it is the next to last picture after clicking left, then it means that it was just on the last picture, where the right button would be disabled. If that is the case, we enable the right button so they can continue forward. Almost the identical actions are applied to the right button, except, again, in reverse.

Step 4 - Testing and Optimization

Now that's about it. The large picture fades in when you click on a thumbnail, you can move left and right, go back to the main gallery by clicking on the large image, and scroll up and down. But wait! After developing in a good browser such as Firefox, you need to test it in other popular browsers such as Safari, IE6, and IE7. I tested them all, and they all worked, except for—surprise!—Internet Explorer 6. When you click on an image and the horizontal controls slide out, they slide out much too far in IE6. For some reason, IE6 thinks that what every other browser thinks is 286px is only 143px. So, change the following code:

$('left').tween('margin-left', '286px');

To this:

if(Browser.Engine.trident4) { $('left').tween('margin-left', '143px'); } else { $('left').tween('margin-left', '286px'); }

Browser.Engine.trident4 returns true if the browser is IE6. Otherwise, it returns false and executes the original code. And the following code:

$('up').setStyle('margin-left', '286px');

To this:

if(Browser.Engine.trident4) { $('up').setStyle('margin-left', '143px'); } else { $('up').setStyle('margin-left', '286px'); }

Now it works on all browsers. At this point, you can also go to the MooTools site and select only the components you used. Even though mootools.js, main.js, main.css, and the HTML file use less than 100kb together, you can always reduce size to optimize the experience for users with a slower internet connection. However, I left the entire MooTools library in the source code for this, so people that want to change things in the gallery can use all of the components.

And there you have it: a working MooTools photo gallery. Remember, that the MooTools documentation is always useful when developing a site with MooTools. I hope this helps out people trying to combine many JavaScript actions into a complete site.

Advertisement