Floated lists with CSS3 and jQuery

2 October 2009, in CSS, Javascript | 19 comments »

We should all be using advanced CSS selectors by now—they make our lifes so much easier! In this quick tutorial, I’m going to explain how you can have a nicely floated list of items. We will use jQuery to make sure IE understands it too.

The markup

Let’s start with a very clean semantic markup, listing some photos:

<div id="page">

	<ul id="food">
		
		<li>
			<img src="pic-1.jpg" width="200" height="133" alt="Wagamama" />
		</li>
			
		<li>
			<img src="pic-2.jpg" width="200" height="133" alt="Strawberries" />
		</li>
			
		<li>
			<img src="pic-3.jpg" width="200" height="133" alt="The Horseshoe" />
		</li>
			
		<li>
			<img src="pic-4.jpg" width="200" height="133" alt="Brick Lane" />
		</li>
			
		<li>
			<img src="pic-5.jpg" width="200" height="133" alt="Portugal" />
		</li>
			
		<li>
			<img src="pic-6.jpg" width="200" height="133" alt="Burgers" />
		</li>
		
	</ul>
	
</div>

So here we basically have a container div, with the id of “page”. Then we have our list with the id of “food” and our list items, each one containing one image.

The CSS

After we’ve added some CSS resets (check the example page for this bit), let’s add some styling to our “page” div:

#page {
	width: 640px;
	padding: 20px 0;
	margin: auto;
	text-align: left;
	}

Here we’ve added a specific width to the container, some top padding and we’ve aligned it to the centre of the page.

Now let’s add some styling to the list:

#food {
	overflow: hidden;
	}

#food li {
	list-style: none;
	width: 200px;
	float: left;
	margin-right: 20px;
	margin-bottom: 30px;
	}

This should be all we need: a) we’ve added an overflow of “hidden” to the ul (because the elements inside it will be floated, this is the quickest way of clearing it); b) we’ve removed the default bullet points from the list items, added some width to them and floated them to the left; c) finally, some margins to add a bit of breathing space to the page.

If you check your page now, the layout is broken. The last item on each row has a right margin too, and there is no need for it.

We can fix it with some cool CSS:

#food li.end-row {
	margin-right: 0;
	}
#food li:nth-child(3n) {
	margin-right: 0;
	}
	
	
#food li.first-row {
	clear: left;
	}
#food li:nth-child(3n+1) {
	clear: left;
	}

Let’s look at it by steps:

  1. We’ve added this part #food li.end-row because some browsers (ahem!) don’t understand the more complex selector, so they will need a class to target those list items. We will add this class to our markup via jQuery in a bit.
  2. We wanted to target every third list item within our list. We accomplish that with this selector: #food li:nth-child(3n). This means that every three li our CSS will do something. (Read more about the :nth-child() pseudo-class on the W3C website.)
  3. #food li.first-row: this selector will just be useful for Internet Explorer, because it needs to have a specific class on the li item so it knows what we’re talking about.
  4. #food li:nth-child(3n+1): this will target every third li item, but starting from the first one. Here we want to clear these li items on the left, so each line starts fresh.

The JavaScript

The JavaScript part is only needed to accommodate Internet Explorer. Because it doesn’t understand the :nth-child selector (yet?), we need to add some very non-semantic classes to the li items we need to target, and we’ll be doing that via jQuery.

So, the next step is to download jQuery and link it to the page, placing this bit on the header of the page:

<script type="text/javascript" src="jquery-1.3.2.min.js"></script>

We will also create our own JavaScript file to instruct the HTML on how to behave with jQuery. Place this after the previous code:

<script type="text/javascript" src="clear-list.js"></script>

Now on our newly created file (clear-list.js), we’ll add this code:

$(document).ready(function () {
							
	$('#food li:nth-child(3n)').addClass('end-row');
	$('#food li:nth-child(3n+1)').addClass('first-row');
	
});

Let’s focus on what’s important here.

We are using jQuery to add a class (end-row and first-row) to specific selectors (#food li:nth-child(3n) and #food li:nth-child(3n+1)), and that’s all you need to know. You can edit those parts according to your own HTML, but it’s quite simple!

Conclusion

In a perfect world, we wouldn’t need to use jQuery to make this code work. But we don’t live in a perfect world, and we have Internet Explorer to thank for that.

With this quick and very simple tutorial (for example, the li items have the same height, so I’m kind of cheating here!) my goal was just to show you can use more advanced CSS selectors, but if your layout breaks or is unusable on some browsers, there are some easy solutions to put it back in place.

You can download the example files here:

View example Download source files

If you’d like me to do more quick demonstrations of CSS selectors, just name them in the comments :)

There are 19 comments:

  1. And if they’re using IE and have JS disabled…?

    :(

  2. inayaili says:

    The layout will break, but the content will be there and be usable. No need to be sad.

    • But, unless you are producing the content dynamically (from a CMS or something) then why not just put these classes in manually?

      Sure, you’ll complain about the fact that you had to, but it will work the same in IE with JS disabled.

      The way it works at the moment will add these classes for every browser with JS enabled anyway.

      Instead of:

      $(document).ready(function () {

      …you can have:

      $(function () {

      …which might be harder to read (at first) but it’s less code. Each to their own.

      I appreciate this is just an example – I shall shut up now! :)

      • inayaili says:

        Yes, I was thinking of cases when the content is generated dynamically, and yes, it’s just an example of how the :nth-child pseudo-selector can be used in real life.

        If you’re prepared to use this type of selectors, I guess you should also be prepared to see different things in different browsers :)

    • dc says:

      You shouldnt use very, clean, semantic, and div together at anytime. I also think this example is over engineered.

  3. Polprav says:

    Hello from Russia!
    Can I quote a post in your blog with the link to you?

  4. Radu says:

    Unfortunately we’re still far off from CSS3 being widely supported so in the real world where layout constraints are tighter and expectations for consistency higher, I still refrain myself from using them, since that means having to cater for IE 6/7 separately.
    So what I try to do most of the time is to find a way that works all browsers without the need for specific hacks or separate approaches through conditional comments.
    In this case, that would be making the UL wider with the amount of right-margin you set on the LI (20px), and then applying a negative margin of 20px to it.

  5. dale says:

    does this technique work fine without jquery in IE8? how is the support in IE8 for CSS3?

  6. lawless says:

    I don’t think the use of jQuery is necessary or even beneficial in this circumstance. If you have a white background, as in your example, the psuedo-class descriptors don’t actually make any difference and if you did have a contrasting background there is no margin or padding on the left or right side so the white-space balance is off.

    It would be much easier/quicker-loading to balance the white space with a left/right margin attribute.

    I love the use of unordered lists for image galleries though and have been using them for years with dynamic database-driven sites.

    Call me old-school, but I don’t want to take the page load hit of jQuery in order to remove a right margin. I’d rather use a cleaner, simpler, faster-loading pure-CSS solution that will render similarly in all browsers.

  7. Matt says:

    Alternatively, you could just use margins and CSS to achieve the same result…

    ul#food { margin 0 -10px; }

    ul#food li { width: 200px; margin: 0 10px; float: left; }

  8. very nice article. Big thanks

  9. johntnorris says:

    I’m all for pushing the web on, but if you provide a fallback solution that works for ALL browsers (jQuery), why have the CSS3 approach at all? All browsers will use the class-based CSS, and some will also unnecessarily use the nth-child selector CSS.

    If you are going to do something like this, why not just target those browsers?:

    if($.browser.msie && $.browser.version < 9){

    $('#food li:nth-child(3n)').addClass('end-row');
    $('#food li:nth-child(3n+1)').addClass('first-row');

    }

    Then you're not forcing unnecessary javascript or additional markup onto the well behaved browsers, and you genuinely will be providing a CSS3 solution for some browsers, and a javascript solution for the rest.

    Personally, I'm all for adding classes of "first" and "last" in the markup. It's dirty, but mostly semantic and most performant, and works in all browsers regardless of whether the user has javascript enabled/not.

    • I agree with adding “first” or “last” etc, until we don’t need them anymore, and my code often looks like this:

      /*when our supported browsers all support nth-child, we can drop the third class*/
      #product-cluster .product.third, #product-cluster .product:nth-child(3n) {margin-right:0;width:264px;}

      And using jQuery.browser is bad practise, which is why your approach is inappropriate:

      http://api.jquery.com/jQuery.browser/

  10. Mara says:

    And what was this article all about? I can do floated lists with “float: left” and that’s it.

  11. jennifer says:

    can lightbox be incorporated with this so the images enlarge on click?

  12. scott says:

    Great tutorial! Solved a problem I spent a couple hours trying to find the solution for this on a wordpress gallery. I was attempting to use the :first-child pseudo class with no luck.

  13. Tarun Sharma says:

    jQuery is good idea but, only if you have 2 – 3 this type of selectors in your application or webpage. Other wise Selectivizr is nice library to support selectors in old browser.

    you just need to include it only for old browsers of IE. Use CSS normally as you use for modern browsers:

    #food li:nth-child(3n) {
    }

    It’ll automatically work on IE old browsers, not need to do anything for it :)

Leave a comment: