October 10th, 2007 - by Paul OB

Tables handle images with captions nicely — you can easily center an image and have a caption centered underneath it while also having the image centered within a container or viewport. However, the drawback of using tables for this is that you are limited to a fixed number of cells across the screen and although the cells can grow and collapse depending on the image size, you can never have any more or less number of images per row than those you started with. If a table is 4 cells wide then you are restricted to 4 images for every row no matter how big or small they are.

When CSS came along a lot of people said “Aha! We can now float our images across the page”. This was a good start as it allowed the images to float across the page and expand or wrap as the page got smaller or bigger. Adding a caption wasn’t that difficult either as the parent container could be floated with the image and caption within it.

In this article we explore some useful CSS techniques to create a small photo gallery that will allow for a caption to be placed underneath our photos. This technique allows our images to flow and re-align across the screen depending on the width of the browser, unlike a table approach which always limits you to the same number of cells per row that you started with.

I should warn you that our code uses a few small work-a-rounds or “hacks – that won’t validate” so look away now if that scares you!

Also, before we begin, I should mention that the major point of this article is to learn about how some CSS properties work and can be manipulated rather than producing anything useful at the end. Now that I’ve got the disclaimers out of the way we can start having some fun.

Let’s start with the code below:

  1. .gallery{
  2. list-style:none;
  3. margin:0;
  4. padding:0
  5. }
  6. .gallery li{
  7. float:left;
  8. border:1px solid #000;
  9. padding:5px;
  10. margin:10px 10px 0 0;
  11. text-align:center
  12. }
  1. <ul class="gallery">
  2. <li><img src="images/image-ex.jpg" width="100" height="100" alt="example image" /><br />
  3. Caption</li>
  4. <li><img src="images/image-ex.jpg" width="100" height="100" alt="example image" /><br />
  5. Caption</li>
  6. <li><img src="images/image-ex.jpg" width="100" height="100" alt="example image" /><br />
  7. Caption</li>
  8. <li><img src="images/image-ex.jpg" width="100" height="100" alt="example image" /><br />
  9. Caption</li>
  10. <li><img src="images/image-ex.jpg" width="100" height="100" alt="example image" /><br />
  11. Caption</li>
  12. <li><img src="images/image-ex.jpg" width="100" height="100" alt="example image" /><br />
  13. Caption</li>
  14. <li><img src="images/image-ex.jpg" width="100" height="100" alt="example image" /><br />
  15. Caption</li>
  16. </ul>

Running the above code will produce the output as shown in Figure 1 below:

figure 1

Figure 1

That looks pretty good already and the code is incredibly simple. The images flow and re-align across the screen depending on the width of the browser unlike a table approach which always limits you to the same number of cells per row that you started with.

Where’s the Snag?

There are three major drawbacks with this floated method:

  1. The first problem is that the captions mustn’t be wider than the images.
  2. The second problem is that the floats can’t be centered in a fluid width environment.
  3. The third problem is a major drawback of this method as our example relies on the images and the captions all being exactly the same height. If you wanted various images across the screen then we would soon run into problems where floats would snag on each other when they wrap to a new line.

Let’s work on the third problem first as it is really a show-stopper if you have images of varying size. Here is a screenshot of what happens when the images are not a uniform height.

figure 2

Figure 2

As you can see the fourth float has snagged on the second float when it tried to wrap. This obviously is a problem and there is no solution when using the floating method. You either have to set a height for the elements so that it accommodates all image sizes or else crop the images to the same size. Sometimes cropping (or clipping) the images is the easiest option, but we will explore a more advanced technique to overcome this problem.

Let’s All Get Inline

We need a totally different approach and as it turns out we have one! Enter display:inline-block. If you haven’t heard of inline-block before then basically what it does is cause an element to generate a box which can flow inline much the same as replaced elements (like images). This means that our block level elements can have widths and heights and also line up together horizontally across the page (like inline elements). Unfortunately, even though display:inline block is valid CSS 2.1 only some browsers have implemented it — Safari and Opera 9 have, but IE and Firefox 2.0 do not support it.

In the case of IE, it’s stated that it does support inline-block, but only on inline elements (which defeats the purpose as it’s usually block elements that you want to be made inline-block). However, IE will actually treat any inline element as display:inline-block when that element has “layout” (see MSDN for more information on HasLayout). So in theory you could set an anchor (which is an inline element by default in HTML) to be display:inline-block simply by applying one of the properties that cause “HasLayout” to be true. For an inline element the only properties that we can really use to do this are, strangely enough, display:inline-block itself or the MS proprietary zoom property (zoom sets the magnification scale of an element; the value 1.0 means no change at all).

This is worth an example so you can test for yourself.

  1. ul{
  2. text-align:center;
  3. list-style:none;
  4. margin:;
  5. }
  6. a{
  7. zoom:1.0;
  8. background:red;
  9. height:100px;
  10. width:100px;
  11. margin:10px;
  12. border:1px solid #000;
  13. }
  1. <ul>
  2. <li>
  3. <a href="#">Only In IE <br /> will this look ok</a>
  4. <a href="#">Only In IE <br /> will this look ok</a>
  5. <a href="#">Only In IE <br /> will this look ok</a>
  6. <a href="#">Only In IE <br /> will this look ok</a>
  7. <li>
  8. </ul>

The result is shown in Figure 3:

figure 3

Figure 3

As you can see, IE now handles the anchors as block elements and not only lines then up nicely horizontally, but will also center them within the container if required. What is useful about this approach is that should the elements be of different heights then they will not snag and will always wrap to a new line under the row above no matter what the size of the images may be.

You may be thinking “Well that’s ok for inline elements, but what about block level elements? What if we want to have more than one anchor in the block?” The answer turns out to be rather simple — all we need to do for a block element is to make it display:inline and then apply “HasLayout” to it. As we have already determined, an inline element with “HasLayout” applied will behave as inline-block. So all we are doing is making the block element generate an inline box and then we apply “HasLayout” to it as before.

See for yourself:

  1. ul{
  2. text-align:center;
  3. list-style:none;
  4. margin:;
  5. }
  6. li{
  7. display:inline;
  8. zoom:1.0;
  9. background:red;
  10. height:100px;
  11. width:100px;
  12. margin:10px;
  13. border:1px solid #000;
  14. }
  1. <ul>
  2. <li><a href="#">Only In IE <br /> will this look ok</a><P>hello</P></li>
  3. <li> <a href="#">Only In IE <br /> will this look ok</a></li>
  4. <li><a href="#">Only In IE <br /> will this look ok</a></li>
  5. <li><a href="#">Only In IE <br /> will this look ok</a></li>
  6. </ul>

The result will be the same as shown in Figure 3 except that we will be able to stack more content inside our list element as required (including multiple anchors) unlike using the anchor which must obey the rules of HTML and only contain inline elements. If you are familiar with “HasLayout” you may be wondering why we need zoom:1.0 in the above when height and width cause “HasLayout” to be true also. Again the answer is simple: height and width only refer to block level elements (or replaced elements like images) and we have just set our list to be display:inline which means height and width don’t apply and therefore “HasLayout” does not get applied. On the other hand, zoom applies to both block and inline elements and applies “HasLayout” in either case. You may also be wondering why we couldn’t use display:inline-block instead, but as already mentioned display:inline-block wouldn’t change the display of the block level element to inline-block because IE only applies it correctly on inline elements. However, it would have set “HasLayout” to be true just to confuse things.

Let’s Recap

To make an inline element behave like inline-block you simply include a property that sets “HasLayout” to be true. This could be display:inline-block itself or another property like zoom:1.0.

To make a block level element behave like inline-block you first set it to display:inline and then you set “HasLayout” to be true by using the proprietary zoom:1.0 property value.

But what about the Fox?

Now that we have got something we can reliably use in IE how are we to deal with Firefox 2.0 as it doesn’t support display:inline-block and is our last stumbling block? This is something I always recommend not doing but as rules are there to be broken I am going to show a method involving “Vendor Specific Extensions”. If you aren’t familiar with this term then all you really need to know is that Vendors (browser makers) have for some time implemented proprietary code into their browsers that they use mainly for testing and developing their browser before the actual properties go live to the general public. Due to their very nature “Vendor Specific Extensions” can’t always be guaranteed to follow the correct format of the CSS specifications in the way that these properties behave and there is no guarantee that the property will not change its behavior before it is implemented and released to the public.

However, as we already know that display:inline-block is a valid property and works in Safari and Opera 9 we can pretty much be assured that Firefox will implement something pretty similar in the future and therefore it is a reasonable gamble that we can use their proprietary version of this. The property we use is called:

display:-moz-inline-box;

This behaves much the same as display:inline-block in ways that will prove useful to us for our gallery display. So for Firefox, Opera and Safari we will use the following code:

display:-moz-inline-box;/* mozilla only */
display:inline-block;/* browsers that support display:inline-block like Safari & Opera*/

For IE we will use conditional comments and supply it with the following code:

display:inline;zoom:1.0

If you’ve been paying attention you should work out why zoom isn’t actually necessary for IE in this case (because we have used display:inline-block in the main style and that gives “HasLayout” to the element anyway).

We now need to bring all this together into a coherent example so it’s time to get dirty with the code.

  1. * {margin:0;padding:0;}
  2. #mainwrap{
  3. background:#ffffcc;
  4. width:80%;
  5. margin:0 0 0 10%;
  6. text-align:center;
  7. border:1px solid #000;
  8. }
  9. #mainwrap ul {
  10. list-style:none;
  11. }
  12. #mainwrap ul li {
  13. padding:5px;
  14. margin:15px 10px;
  15. border:1px solid #000;
  16. background:#809900;
  17. color:#fff;
  18. font-weight:bold;
  19. display:-moz-inline-box;/* mozilla only */
  20. display:inline-block;/* browsers that support display:inline-block like Safari &amp; Opera*/
  21. vertical-align:bottom;/* so that captions line up */
  22. }
  23. #mainwrap ul li img{border:1px solid #000;margin:5px;}

IE only CSS (inside conditional comments):

  1. <!--[if IE ]>
  2. <style type="text/css">
  3. #mainwrap ul li{display:inline;zoom:1.0}
  4. * html #mainwrap ul li{height:0}/* IE5 fix*/
  5. </style>
  6. <![endif]-->
  1. <div id="mainwrap">
  2. <h3>Elements of varying height and width.</h3>
  3. <ul>
  4. <li>
  5. <p><a href="#"><img src="images/image-ex.jpg" width="113" height="76" alt="Example image" /></a><br />
  6. Caption that is <br />
  7. split onto 2 lines or<br />
  8. more </p>
  9. </li>
  10. <li>
  11. <p><a href="#"><img src="images/image-ex.jpg" width="163" height="96" alt="Example image" /></a><br />
  12. Caption </p>
  13. </li>
  14. <li>
  15. <p><a href="#"><img src="images/image-ex.jpg" width="153" height="46" alt="Example image" /></a><br />
  16. Caption </p>
  17. </li>
  18. </ul>
  19. </div>

If you run the above code after adding images of various sizes you will get a display similar to that shown in figure 4 below:

figure 4

Figure 4

As you can see we now have nicely centered block level elements that align along the baseline and wrap to the next line very neatly no matter what the height of the image is. If the browser window is opened or closed then the images will flow to the next preceding line as required without upset.

There are a couple of things to mention: first, as these elements now behave like inline-block elements we can center them by adding text-align:center on the parent container. They will then automatically center without any further work needed.

Next we can apply vertical alignment to these elements just by using vertical-align because they’re inline elements. I have aligned the elements along their bottom edge by using vertical-align:bottom.

Here is a link to a demonstration.

Falls at the Last Hurdle

Now that is almost perfect — it allows you to have different links for the images and captions (should you ever want something so silly). The eagle-eyed among you will have spotted the only downfall of this method — if you look at the code you will see that where captions are longer than the image I have inserted breaks into the code to make the text drop to another line.

e.g. Here:

  1. <p><a href="#"><img src="images/image-ex.jpg" width="113" height="76" alt="Example image" /></a><br />
  2. Caption that is <br />
  3. split onto 2 lines or<br />
  4. more </p>

Although the display handles this perfectly and the rows line up no matter how many lines we put the caption on it would be nice if we could do this wrapping automatically and without specifying a width. Unfortunately this is not possible for IE browsers at all but there is a method we can use for Firefox 2.0, Opera 9+ and Safari 1.2+ so I still think it’s worth pursuing as it also shows another new technique.

You may have noticed that the HTML for my example shows an unnecessary p element holding the image and caption and that’s because we are going to make use of it now. We needed to have a container without width that was stretched to fit any width of image and we have done this nicely. However, now we want a caption underneath the image and the caption needs to stay within the same width of the image and wrap to the next line when the image width is reached. This may sound impossible, but I have one more trick up my sleeve. We can copy a table behavior where the contents in a cell will always fit even if the table width is set to 1px. Using this method we turn our p element to display like a table using display:table and then we set a width of 1px which will expand to hold the caption but at the same time wrap at the image width.

All we need to add to our demo is this one line of code:

#mainwrap ul li p{width:1px;display:table;}

Now Firefox 2.0, Opera 9 and Safari have a totally automated gallery that cannot be accomplished with tables at all. The image’s width and height can vary and the caption can be any length and wrap at the image’s width automatically. The elements will behave similarly to floats except that they are centered and don’t snag.

Here is the living proof.

I’m afraid that there’s nothing we can do for IE with regard to the caption wrapping. IE will just have to let the caption stretch wide or amend the HTML to break the line up using break tags. It doesn’t look too bad in IE and here are two screenshots so you can compare IE and Firefox.

figure 5

Figure 5 – IE Screenshot

In Figure 5 you can see that the first caption is stretching the container to accommodate the text. As I said above there’s not much we can do about this apart from setting a width (which we don’t want to do), or to insert break tags in the HTML. Usually a caption for an image is going to be short anyway and I can live with the odd instance of the above for now.

In Figure 6 we see the behavior in Firefox 2, Safari and Opera 9.

figure 6

Figure 6 – Firefox 2.0

Firefox is behaving exactly as we want and we now have a layout that seems to defy what we thought was possible with CSS. It certainly cannot be done with tables due to the fluid wrapping and number of items per row effect.

Is it Safe?

Is it safe to use? The question you first need to ask yourself is: “Can I do this any other way without using any ‘hacks’ ?”, or “Can I change the design so that it doesn’t need any hacks?”. If the answer is no then you have to consider the risks in using it.

First of all we are using a “vendor specific extension” (-moz-inline-block) which is not guaranteed to work or may even be changed before the real property is implemented correctly. However, we do have a fall-back of using the correct CSS for this property (display:inline-block) so that when it does get implemented then it should work correctly because we are already using it correctly in browsers that support it. I can tell you that Firefox 3 versions that are being tested at the moment do display the page correctly. Opera 9 and Safari display the page correctly using the correct property and don’t need any hacks so they are safe.

For IE we are using a technique that works all the way from IE5(Win) – IE7 so there is a good chance that this will continue to work as long as we need it. If IE8 (or whatever it will be) is improved and supports display:inline-block then we will also be ok. (Older browsers like IE5(mac) won’t like the techniques we’ve shown here at all.)

However, nothing is ever certain and things do change over time and it’s a choice you must make for yourself based on the circumstances at the time. My advice is usually to avoid using anything that isn’t valid unless there is absolutely no other choice and when the job you want done can’t be done by any other means.

As I said at the beginning, this article wasn’t really about producing anything useful but at exploring what could be done and hopefully covering new ground on the way. Whether or not you use the techniques here I hope you learned something interesting on the way.

4 Responses to “Tables – Anything You Can Do I Can Do Better”

1 Paul OB

I’ve added 2 more examples that show how to make this approach work with a fixed width for the image and also with a fixed height and width for the image.

http://www.pmob.co.uk/search-this/stcaption3.htm
http://www.pmob.co.uk/search-this/stcaption4.htm

2 Scrolling, Scrolling, Scrolling

[…] first method I am going to show is one that we have used before and involves using display:inline-block. As Firefox 2.0 doesn’t understand display:inline-block we […]

3 Rich

I can’t seem to get the grid-like behavior to work when I put all this style into into a .css file. It still looks more like Figure2. I’m using FF2.

4 ryan

I am so sick of people saying CSS is better for everything…face it people, CSS HAS IT’S FLAWS TOO!!! For some things, especially alignment, tables are SOO much better!

mulberry sale spyder womens jacket cheap new balance 574 mulberry outlet cheap new balance 574 arcteryx outlet mulberry sale spyder womens jacket mulberry sale spyder womens jacket mulberry outlet mulberry outlet new balance 574

Popular Articles

Top 10 Commentators


Subscribe to this feed! Subscribe by Email!

Random Bits Podcast

You need to download the Flash player from Adobe

Blogs Worth Reading