Setting Equal Heights with jQuery

Posted by Maggie and Scott on 07/25/2008

Topics:

We wrote a script to "equalize" the heights of boxes within the same container and create a tidy grid — with little overhead.

Creating the visual effect of equal-height columns or content boxes has been a challenge ever since we abandoned table-based layouts. When developing complex web applications or site designs we've found that it often makes the most sense from a usability and performance standpoint to use a simple JavaScript workaround: our equalHeights() function determines the heights of all sibling elements in a container, and then sets each element's minimum height to that of the tallest element. When JavaScript is disabled, the boxes or columns appear with varying heights, but the content remains legible and the page is still completely usable.

Why not just use CSS?

We advocate using CSS whenever possible, and we often can because there are many clever ways to create the illusion of equal-height boxes without resorting to setting a fixed height, like Dan Cederholm's faux columns. This and similar techniques are great for columns in a page layout, but they don't translate very well when we're building, say, a web application dashboard page where 4 small widget boxes sit in a "row." In this case we could create an uber tiling background image that has backgrounds for 4 columns, but we'd also have to design the boxes with square corners (or make a big sliding door), fix the widths of each box, and make sure the markup lines up exactly with the background image. Or, because CSS2 only supports one background image per element, we could nest 4 separate divs, each with it's own positioned background image. No matter how we approach it, we've yet to come across a CSS-only solution to this use case that doesn't require pixel-exact calculations when creating the background images, or a significant amount of unnecessary markup. Let's not even discuss what you have to do when the boxes can vary in width, too.

JavaScript to the rescue

Calling equalHeights() on DOM ready let's us keep all of the important factors in play, without adding extra markup or complex CSS workarounds:

  • usability. This is strictly a visual effect, so when the script is absent, columns are of varying heights and the page remains totally usable.
  • layout flexibility. The script assigns a min-height value (not height), so when content is added through user interaction or via AJAX running in the background, or if the user increases the browser text size, the content box will grow to fit. Columns or boxes can keep their percentage or em-based widths.
  • performance. It's lightweight — has a small footprint, and doesn't insert any DOM elements or require extra markup — and unobtrusive because, like other jQuery plugins, it's called on standard CSS selectors.

How it works

equalHeights() loops through the top-level child nodes of a specified element and sets their min-height values to that of the tallest. It's set up as a standard jQuery plugin, and is called on the container element:

$('.container').equalHeights();

In our version of the script the default unit is set to ems so that the content boxes will scale. This requires another of our methods, pxToEm; if it's not present, the default unit reverts to pixels. Or, if you're using pxToEm, you can override the default and pass a "true" argument to set the unit in pixels.

The script

Enjoy (with proper attribution, please).

/*-------------------------------------------------------------------- 
 * JQuery Plugin: "EqualHeights"
 * by:	Scott Jehl, Todd Parker, Maggie Costello Wachs (http://www.filamentgroup.com)
 *
 * Copyright (c) 2008 Filament Group
 * Licensed under GPL (http://www.opensource.org/licenses/gpl-license.php)
 *
 * Description: Compares the heights or widths of the top-level children of a provided element 
 		and sets their min-height to the tallest height (or width to widest width). Sets in em units 
 		by default if pxToEm() method is available.
 * Dependencies: jQuery library, pxToEm method	(article: 
		http://www.filamentgroup.com/lab/retaining_scalable_interfaces_with_pixel_to_em_conversion/)							  
 * Usage Example: $(element).equalHeights();
  		Optional: to set min-height in px, pass a true argument: $(element).equalHeights(true);
 * Version: 2.0, 08.01.2008
--------------------------------------------------------------------*/

$.fn.equalHeights = function(px) {
	$(this).each(function(){
		var currentTallest = 0;
		$(this).children().each(function(i){
			if ($(this).height() > currentTallest) { currentTallest = $(this).height(); }
		});
		if (!px || !Number.prototype.pxToEm) currentTallest = currentTallest.pxToEm(); //use ems unless px is specified
		// for ie6, set height since min-height isn't supported
		if ($.browser.msie && $.browser.version == 6.0) { $(this).children().css({'height': currentTallest}); }
		$(this).children().css({'min-height': currentTallest}); 
	});
	return this;
};

Comments

This is great! I actually have an immediate use for this technique.

Comment by Nosredna on 07/25  at  02:13 PM

We were in the process of writing a script to do this so f-ing thanks!

This should make the perfect study on how to turn some tacked-on code into an actual plug-in.

( one next step might be to get this into the tabs UI component as the default behavior )

Comment by Scott Fitchet on 07/25  at  02:28 PM

I published a similar script a while back. My script takes an arbritrary collection of elements, rather than acting on top-level children of a container element. I think your use case is by far the most common, but I recently had a need to equalize elements that didn’t have a common parent.

I recently updated my demo page using my new “onfontresize” custom event script to ensure that the element heights are re-equalized when the user resizes her fonts.

http://tomdeater.com/jquery/equalize_columns/

Comment by Tom Deater on 07/28  at  01:19 PM

nice, Ill give a try now, thanks for sharing

Comment by acms on 08/02  at  03:00 PM

Is it possible to resize the elements after text has been resized in a browser?

Comment by Andrew Care on 08/16  at  06:12 PM

Despite this:
and sets their min-height to the tallest height (or width to widest width). Sets in em units by default if pxToEm() method is available.

I can’t figure if it’s already set in em unit because when I enlarge font-size, I am getting unequal height.
With ‘true em unit’ you will always get the same ‘equal height’.

Nice though.

Comment by sutra on 09/19  at  08:15 AM

Perhaps you should check out my nested container equal height columns with pure CSS, these should do everything you need without the JavaScript.

Comment by Matthew James Taylor on 10/24  at  10:23 PM

@Matthew James Taylor:

I like your solution, however the problem with it is that not all CMS systems can spit out HTML in that sort of way. Some will only generate content per object, and therefore you can’t predict - ahead of time, how many containers you will have. The best you can do is to spit the objects out and wrap them in a common element (the .container).

This solution really solves that problem, since you can spit out an x-number of containers, and then toggle the JS over all those containers for them to equalize.

Comment by Anton Babushkin on 11/09  at  06:53 PM

This works great.  I had grid of floated div’s that I wanted to fill a dynamic width then wrap. Unfortunately the wrapping was occassionally out of wack if the heights varied just slightly, not anymore. Nicely done, and much thanks!

Comment by Inlime on 11/18  at  07:45 PM

When browser checking on firefox 2.0.0.17 I did find some odd behavior. While this may only be related to the way I am using it, I thought I’d share that changing lines 29:30 to the following fixed my issue:

jQuery(this).children("div").css({ “height” : currentTallest, “min-height” : currentTallest });

From what it seems it didn’t like children() without an element.

Comment by Inlime on 11/19  at  09:26 AM

Add a Comment:* required fields