Skip to content Skip to tags Skip to twitter news Skip to blog roll Skip to categories Skip to archives Skip to recent posts

April 5, 2008

none

Create an unobtrusive photo gallery

by in Accessibility,jQuery,Photogallery,Tutorials on April 5, 2008 @ 12:00 am

As you very well know by now, I have created quite a few [tags]image / photo galleries[/tags] and the ultimate goal was to allow them to be as unobtrusive as possible. Well, the other day I got into a discussion with a co-worker and the questions he asked were, “How is that unobtrusive?”, referring to my [tags]gallery[/tags] [tags]plugins[/tags], “The images are in list elements?” My answer to that was, “They can be styled to have each list element float and punched up a bit.” His rebuttal, “But what if styles were off also?” I hesitated for a second and he knew he had won this conversation and silently walked away.

The purpose

So the purpose of this post is a) show you how to build an unobtrusive image gallery or photo gallery, how ever you want to say it, and b) show you have to create a jQuery plugin.

Step 1

So what I am going to do here is show you have to create an unobtrusive image / [tags]photo gallery[/tags] using a, “gasp,” table. Why a table? Tables are so 1.0? Well, the definition of [tags]unobtrusive[/tags] basically breaks down to, if the user has JavaScript AND CSS off, the gallery should still work and be laid out as planned.

Below is the structure we are going to start of with; nothing fancy, just old fashion html. Each image sites in a cell of the table which we can control the basic look by adjusting the cellpadding or cellspacing attributes.

  1. <table id="theGallery" border="1">
  2.     <caption>
  3.         The Gallery
  4.     </caption>
  5.     <tbody>
  6.         <tr>
  7.             <td><a href="http://lh6.google.com/sterling.benjamin/RuyW_7tKn8I/AAAAAAAAAVc/opzOXldgJ0Y/DSC_0001.JPG?imgmax=800"><img src="http://lh6.google.com/sterling.benjamin/RuyW_7tKn8I/AAAAAAAAAVc/opzOXldgJ0Y/s72/DSC_0001.JPG" alt="Yes, I do know what I am doing!" width="72" height="48" border="0"/></a></td>
  8.             <td><a href="http://lh5.google.com/sterling.benjamin/RuyYPrtKn9I/AAAAAAAAAWE/Y1kITpSTFXI/DSC_0025.JPG?imgmax=800"><img src="http://lh5.google.com/sterling.benjamin/RuyYPrtKn9I/AAAAAAAAAWE/Y1kITpSTFXI/s72/DSC_0025.JPG" alt="Hey, it is better then reversible diapers!" width="72" height="48" border="0"/></a></td>
  9.             <td><a href="http://lh6.google.com/sterling.benjamin/RuyYQ7tKn-I/AAAAAAAAAWM/PHN5lywzjbU/100_1712.JPG?imgmax=800"><img src="http://lh6.google.com/sterling.benjamin/RuyYQ7tKn-I/AAAAAAAAAWM/PHN5lywzjbU/s72/100_1712.JPG" alt="It... it smells like... old cheese in here..." width="72" height="54" border="0"/></a></td>
  10.             <td><a href="http://lh6.google.com/sterling.benjamin/RuyYR7tKn_I/AAAAAAAAAWU/bJ-HZwkTyKg/100_1886.JPG?imgmax=800"><img src="http://lh6.google.com/sterling.benjamin/RuyYR7tKn_I/AAAAAAAAAWU/bJ-HZwkTyKg/s72/100_1886.JPG" alt="Everyone looks good through a glass of beer!" width="72" height="54" border="0"/></a></td>
  11.             <td><a href="http://lh4.google.com/sterling.benjamin/RuyZObtKoAI/AAAAAAAAAWc/dDMbJK0jSlw/DSC_0004.JPG?imgmax=800"><img src="http://lh4.google.com/sterling.benjamin/RuyZObtKoAI/AAAAAAAAAWc/dDMbJK0jSlw/s72/DSC_0004.JPG" alt="Sample Text" border="0"/></a></td>
  12.         </tr>
  13.         <tr>
  14.             <td><a href="http://lh6.google.com/sterling.benjamin/RuyZa7tKoBI/AAAAAAAAAWo/dmnMNJS2nPI/DSC_0005.JPG?imgmax=800"><img src="http://lh6.google.com/sterling.benjamin/RuyZa7tKoBI/AAAAAAAAAWo/dmnMNJS2nPI/s72/DSC_0005.JPG" alt="Sample text" border="0"/></a></td>
  15.             <td><a href="http://lh3.google.com/sterling.benjamin/RuyZnLtKoCI/AAAAAAAAAWw/1TD-_nNT8PA/DSC_0006.JPG?imgmax=800"><img src="http://lh3.google.com/sterling.benjamin/RuyZnLtKoCI/AAAAAAAAAWw/1TD-_nNT8PA/s72/DSC_0006.JPG" alt="Sample text" width="72" height="48" border="0"/></a></td>
  16.             <td><a href="http://lh5.google.com/sterling.benjamin/RuyZ2rtKoDI/AAAAAAAAAW8/qo9vOy-0mcI/DSC_0012.JPG?imgmax=800"><img src="http://lh5.google.com/sterling.benjamin/RuyZ2rtKoDI/AAAAAAAAAW8/qo9vOy-0mcI/s72/DSC_0012.JPG" alt="Is he going to eat me?!" border="0"/></a></td>
  17.             <td><a href="http://lh6.google.com/sterling.benjamin/RuyaB7tKoEI/AAAAAAAAAXE/Yt124tfI_WU/DSC_0030.JPG?imgmax=800"><img src="http://lh6.google.com/sterling.benjamin/RuyaB7tKoEI/AAAAAAAAAXE/Yt124tfI_WU/s72/DSC_0030.JPG" alt="Sample text" width="72" height="48" border="0"/></a></td>
  18.             <td><a href="http://lh4.google.com/sterling.benjamin/RuyajbtKoFI/AAAAAAAAAXQ/QfKtfA6yEwE/DSC_0055.JPG?imgmax=800"><img src="http://lh4.google.com/sterling.benjamin/RuyajbtKoFI/AAAAAAAAAXQ/QfKtfA6yEwE/s72/DSC_0055.JPG" alt="It's like talking to a wall!!!" width="72" height="48" border="0"/></a></td>
  19.         </tr>
  20.         <tr>
  21.             <td><a href="http://lh5.google.com/sterling.benjamin/RuyaurtKoGI/AAAAAAAAAXY/t_ASBQcH07k/DSC_0047.JPG?imgmax=800"><img src="http://lh5.google.com/sterling.benjamin/RuyaurtKoGI/AAAAAAAAAXY/t_ASBQcH07k/s72/DSC_0047.JPG" alt="Sample text" width="72" height="48" border="0"/></a></td>
  22.             <td><a href="http://lh6.google.com/sterling.benjamin/Ruya87tKoHI/AAAAAAAAAXk/LJZOZR7RmSU/DSC_0032.JPG?imgmax=800"><img src="http://lh6.google.com/sterling.benjamin/Ruya87tKoHI/AAAAAAAAAXk/LJZOZR7RmSU/s72/DSC_0032.JPG" alt="Sample text" width="72" height="48" border="0"/></a></td>
  23.             <td><a href="http://lh4.google.com/sterling.benjamin/RuybGbtKoII/AAAAAAAAAXs/dBiWzDZfWas/DSC_0033.JPG?imgmax=800"><img src="http://lh4.google.com/sterling.benjamin/RuybGbtKoII/AAAAAAAAAXs/dBiWzDZfWas/s72/DSC_0033.JPG" alt="This is my seductive look! It works on all the boys." width="72" height="48" border="0"/></a></td>
  24.             <td><a href="http://lh3.google.com/sterling.benjamin/RuybSLtKoJI/AAAAAAAAAX4/btmxxtIcX5I/DSC_0020.JPG?imgmax=800"><img src="http://lh3.google.com/sterling.benjamin/RuybSLtKoJI/AAAAAAAAAX4/btmxxtIcX5I/s72/DSC_0020.JPG" alt="Sample text" border="0"/></a></td>
  25.             <td><a href="http://lh5.google.com/sterling.benjamin/RuybertKoKI/AAAAAAAAAYA/Tay8MWRdwrM/DSC_0021.JPG?imgmax=800"><img src="http://lh5.google.com/sterling.benjamin/RuybertKoKI/AAAAAAAAAYA/Tay8MWRdwrM/s72/DSC_0021.JPG" alt="Sample text" border="0"/></a></td>
  26.         </tr>
  27.         <tr>
  28.             <td><a href="http://lh3.google.com/sterling.benjamin/Ruyd3LtKoLI/AAAAAAAAAYM/lOMruypZVd0/DSC_0017.JPG?imgmax=800"><img src="http://lh3.google.com/sterling.benjamin/Ruyd3LtKoLI/AAAAAAAAAYM/lOMruypZVd0/s72/DSC_0017.JPG" alt="Sample text" width="72" height="48" border="0"/></a></td>
  29.             <td><a href="http://lh5.google.com/sterling.benjamin/RuyghrtKoMI/AAAAAAAAAYs/KeOhIMfMrSc/DSC_0024.JPG?imgmax=800"><img src="http://lh5.google.com/sterling.benjamin/RuyghrtKoMI/AAAAAAAAAYs/KeOhIMfMrSc/s72/DSC_0024.JPG" alt="You won't like me when I'm angry!" width="72" height="48" border="0"/></a></td>
  30.             <td><a href="http://lh5.google.com/sterling.benjamin/RuygrrtKoRI/AAAAAAAAAZY/KBrh0d_jECY/DSC_0018.JPG?imgmax=800"><img src="http://lh5.google.com/sterling.benjamin/RuygrrtKoRI/AAAAAAAAAZY/KBrh0d_jECY/s72/DSC_0018.JPG" alt="Sample text" width="72" height="48" border="0"/></a></td>
  31.             <td><a href="http://lh4.google.com/sterling.benjamin/RuygtbtKoSI/AAAAAAAAAZg/99k8UFMs7zU/DSC_0021.JPG?imgmax=800"><img src="http://lh4.google.com/sterling.benjamin/RuygtbtKoSI/AAAAAAAAAZg/99k8UFMs7zU/s72/DSC_0021.JPG" alt="It is out of focus; let me help." width="72" height="48" border="0"/></a></td>
  32.             <td><a href="http://lh4.google.com/sterling.benjamin/RuygxbtKoUI/AAAAAAAAAZw/Htm20GnEPKs/DSC_0007.JPG?imgmax=800"><img src="http://lh4.google.com/sterling.benjamin/RuygxbtKoUI/AAAAAAAAAZw/Htm20GnEPKs/s72/DSC_0007.JPG" alt="I always have to carry the conversation; they never say anything." width="72" height="48" border="0"/></a></td>
  33.         </tr>
  34.         <tr>
  35.             <td><a href="http://lh6.google.com/sterling.benjamin/Ruygy7tKoVI/AAAAAAAAAZ4/ShwLc51HYjQ/DSC_0002.JPG?imgmax=800"><img src="http://lh6.google.com/sterling.benjamin/Ruygy7tKoVI/AAAAAAAAAZ4/ShwLc51HYjQ/s72/DSC_0002.JPG" alt="Sample text" border="0"/></a></td>
  36.             <td><a href="http://lh4.google.com/sterling.benjamin/RuyhzbtKocI/AAAAAAAAAa0/lcEfHJtN67c/DSC_0096.JPG?imgmax=800"><img src="http://lh4.google.com/sterling.benjamin/RuyhzbtKocI/AAAAAAAAAa0/lcEfHJtN67c/s72/DSC_0096.JPG" alt="Sample text" width="72" height="48" border="0"/></a></td>
  37.             <td><a href="http://lh5.google.com/sterling.benjamin/Ruyg6rtKoaI/AAAAAAAAAak/LJmQ1uFC3Y4/DSC_0010.JPG?imgmax=800"><img src="http://lh5.google.com/sterling.benjamin/Ruyg6rtKoaI/AAAAAAAAAak/LJmQ1uFC3Y4/s72/DSC_0010.JPG" alt="Sample text" width="72" height="48" border="0"/></a></td>
  38.             <td><a href="http://lh5.google.com/sterling.benjamin/RuyiPrtKomI/AAAAAAAAAcM/o7St6jYPLEw/DSC_0039.JPG?imgmax=800"><img src="http://lh5.google.com/sterling.benjamin/RuyiPrtKomI/AAAAAAAAAcM/o7St6jYPLEw/s72/DSC_0039.JPG" alt="Sample text" width="72" height="48" border="0"/></a></td>
  39.             <td><a href="http://lh6.google.com/sterling.benjamin/RuykW7tKo0I/AAAAAAAAAeA/Xuw9LFqcfQ4/DSC_0126.JPG?imgmax=800"><img src="http://lh6.google.com/sterling.benjamin/RuykW7tKo0I/AAAAAAAAAeA/Xuw9LFqcfQ4/s72/DSC_0126.JPG" alt="I SAID CLOSER!!!" width="72" height="48" border="0"/></a></td>
  40.         </tr>
  41.     </tbody>
  42. </table>

Current progress

I am using the caption element to give the gallery a title, you could have simply done the below as well:

  1. <thead>
  2.     <tr>
  3.         <th colspan="5">The Gallery</th>
  4.     </tr>
  5. </thead>

Step 3: The Style

Since we are planning for this gallery to still look good if JavaScript is turned off, we are going to need to give it some style, make it pretty. At this point, you are only limited by your imagination and, well IE 6, but that should never hold you back. That is another topic for another day.

We are going to keep it simple and just add some very basic styles:

  1. <style>
  2.     table{
  3.         border:10px solid #000;
  4.         padding:5px;
  5.         background:#fff;
  6.     }
  7.     table td{
  8.         border:none;
  9.         margin:2px;
  10.     }
  11.     table caption{
  12.         font:bold 12px Verdana, Arial, Helvetica, sans-serif;
  13.         text-align:left;
  14.     }
  15.     table td a img{
  16.         padding:2px;
  17.     }
  18.     table td a:hover img{
  19.         padding:0px;
  20.         border:2px solid #f00;
  21.     }
  22. </style>

Current progress

As it stands now, you can leave well enough alone and for the most part, people will be happy with your creation. They have a simply style, low bandwidth, accessible and functional photogallery that will do exactly what you want, display images and open an image when clicked.

But lets, kick it up an notch and add in the JavaScript; enter jQuery and the beauty of plugins.

Step 4: The JavaScript and the framework that we love, jQuery

For those who don’t know what jQuery is, it is a JavaScript framework that helps you “write less, do more.” Have a look over at http://jquery.com for more info.

We are going to create our plugin now that will take our current gallery and spice it up.

The basic pattern for a jQuery plugin is as followed:

  1. (function($){
  2.     $.fn.yourPlugin = function(){
  3.         return this.each(function(index){
  4.         // code here
  5.         });
  6.     };
  7. })(jQuery);

Here we are using closures, I am not an expert, so I’ll post some links at the bottom to other resources to help you understand why we are using this structure. But this is the structure we will be using for our plugin. What I can tell you is that the return this part of the code makes this plugin chainable.

We are going to keep this very simple, so we will not be passing any options or are we going to be creating any really complex situations.

What we are going to do is create some structure, get some data, and add some functionality.

The Variables

We will start out with creating some variables for use through-out our plugin:

  1. var element = this;
  2. element.index = index;
  3. var $this = $(this);
  4. var $img = $('img', $this);

The purpose of “var element = this;” is to keep us from getting confused when using this in other parts of the code.

The purpose of “element.index = index;” is for if we have more then one gallery on a page we will be able to reference the correct one if needed.

The purpose of “var $this = $(this);” is so that we don’t have to do $(this) multiple times slowing down our plugin. This way, we can do $this.Method() and jQuery will not have to keep looking for what this is.

The purpose of “var $img = $(‘img’, $this);” is to create a reference to all the images that are within our element.

The Structure

Our structure is going to be very simple and will look like:

  1. <div class="wrap">
  2.     <div class="head"/>
  3.     <div class="container">
  4.         <div class="imageItem">
  5.             <img width="" height="" border="0" alt="" src=""/>
  6.         </div>
  7.     </div>
  8.     <div class="imageContainer">
  9.         <img class="fullsize"/>
  10.     </div>
  11. </div>

With the DIV of “imageItem” having the same count as the images in your default gallery.

Why not just use the structure that is already created with the table? I personally feel you get more flexibly to style the Gallery and really punch it up by restructuring the gallery.

Lets get to building this structure:

  1. element.wrap = $('<div class="wrap">');
  2. element.head = $('<div class="head">').appendTo(element.wrap);
  3. element.container = $('<div class="container">').appendTo(element.wrap);
  4. element.imageItem = $('<div class="imageItem">');
  5. imageContainer = $('<div class="imageContainer">').appendTo(element.container);
  6. element.fullsize = $('<img class="fullsize"/>').appendTo(element.imageContainer);
  7.  
  8. // img code here
  9.  
  10. $this.after(element.wrap).remove();

The Thumbnails

Next up is getting our images and their respective links and then add our click event:

  1. $img.each(function(i){
  2.     var jqimg = $(this);
  3.     var img = this;
  4.     img.index = i;
  5.     img.fullurl = jqimg.parent().attr('href');
  6.     img.height = jqimg.height();
  7.     img.width = jqimg.width();
  8.    
  9.     element.imageItem.clone()
  10.     .append(jqimg)
  11.     .appendTo(element.container);
  12.    
  13.     jqimg.click(function(){
  14.         element.fullsize.get(0).src = img.fullurl;
  15.     });
  16. });

Ok, what do we have here, line 1 we start to loop through our images, line 2 setting a reference to the jQuery object for the image.

Line 3 is a reference to this.
Line 4 is our index
Line 5 is our reference to the link url

Line 6 and 7 is the height and width, this could be done a different way by create a new Image and creating a onload event where you grab the height and width and have the option to have a “loader” animated gif or notice text. I did nether to keep this as simple as possible.

You will notice starting at line 9 that I am using what is called chaining, this is a process of linking one method to another. But also notice that I put the new methods on separate lines. Why would I do this when I could have chained the one right after another? Because you actually need to be able to read your code. Although chaining is great and you can chain a ton of methods one right after another after another, if you can’t follow what is going on, it is pointless.

You will see at line 9 I am using the clone(), this allows me to quick duplicate the DIV that I created in my structure code.

Line 10 I am appending the image to the newly cloned DIV and at line 11 I am appending both to the container that is housing our thumbnails.

At line 13 I bind a click event, which can also be done by doing $(SELECTOR).bind(‘click’,function(){});, to the thumbnail.

Inside that click event at line 14 I take the reference to the larger image and set the src to the full url that I collected at line 5. But notice that I am using the .get(0) method with the .src attribute and not doing $(SELECTOR).attr(‘src’,img.fullurl);. Why did I do it this way? To show you another example of the flexibility of
jQuery. JavaScript has a ton of built in attributes for each DOM element and using $(SELECTOR).get(0) is more or less the equivalent of getElementById. The 0 is referencing the first element found, so if you want to get the fifth element found, you can do .get(4).

Lets take a look at our Current progress. (note: this is unstyled and will just be a single column of thumbnails with the larger showing up at the bottom when a thumbnail is clicked. We will cover the styling in another post because there will be more then style that will need to be done.)

What you will see in the final example is just a list of thumbnails and once clicked you will see the full size image appear.

The Follow-up

I know I covered a lot and I was trying to keep it as simple as possible, so let review what we did. We create a basic photogallery using basic (x)Html and styles that will work fine if JavaScript is turned off or if a Screen-reader was used to view the photogallery. We then took that gallery and with the help of JavaScript and jQuery we created a very basic photogallery and a reusable jQuery plugin. We learned how to use some of jQuery’s basic methods to grab an element and do something with it.

What we did not do is actually quite a lot. We did not add styles. We did not add any indicators that an image is loading. We did not resize the large image to fit with in a confined area. We did not create any preloading code that will preload the next large image in our list.

The Closing

I will be writing another post that will take care of our “We did not do” items. The purpose of the post was to show you how to build your own plugin and even how to build an effective photogallery.

The may be some items that you don’t understand or would like me to elaborate on, feel free to let me know what those items are and I will try to clarify.

back to beginning of this post back to skip to links

If you liked this article why don't you share it:

Stumble it delicious Digg it Reddit it DZone it Bump it Mixx it! Buzz up! E-mail

Comments are closed.


Learn from my mistakes, I got burnt by the flame, you don't have to.


RSS Feed Link My Hosting of Choice

66 queries. 0.327 seconds. Powered by WordPress visitor stats