Setting Equal Heights with jQuery
Posted by Maggie and Scott on 07/25/2008
- Topics:
- jQuery
- visual design
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;
};
Enjoy our blog? You'll love our book.
For info and pre-order: Visit the book site
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
Your script doesn’t work in my IE 6.
A,C & D are equal but B is taller.
Comment by Al on 11/21 at 05:55 PM
This is awesome. Is there a way to make this work with rounded corners? How about with borders AND rounded corners?
Comment by Patrick Elward on 01/18 at 02:13 PM
Nice!
Comment by longstep on 01/19 at 03:16 AM
This is cool. Can it be made to act on certain classes rather than all children of and element?
Comment by velocitykendal on 01/22 at 10:03 AM
Great!!! Will try this
Comment by dsignz media on 02/09 at 05:13 AM
I was having issues with the script starting before the images were loaded and so the min-height that got calculated was wrong. This is what I did to fix it:
$(function(){
if ($(’img’).length) {
$(’img’).load(function() {
$(’#column_wrap’).equalHeights();
});
} else {
$(’#column_wrap’).equalHeights();
}
});
Comment by Nate on 02/09 at 06:09 PM
Great little script.
Very useful! Thank you
Comment by Omdu on 02/10 at 03:03 PM
Thanks for the work - having an issue in Google Chrome though - it’s making the columns equal height, but there’s added padding. I assume it’s not calculating the height correctly?
Comment by chief on 02/16 at 03:08 PM
I find this script very useful, but it has an important issue: It assumes that the elements share the same padding size.
I had to change height() by innerHeight() when getting the currentTallest:
$(this).children().each(function(i){
__if($(this).innerHeight() > currentTallest) {
____currentTallest = $(this).innerHeight();
__}
});
And then change children with an each(), to check each one’s padding before setting it’s height.
var cssname = ‘min-height’;
if ($.browser.msie && (ie6)) { cssname = ‘height’; }
$(this).children().each(function(i){
__$(this).height(currentTallest -
_______________($(this).innerHeight() - $(this).height()));
});
If you’d like to include the borders too, then you should change innerheight with outerheight.
Comment by hernan on 03/12 at 10:22 AM
Very nice job!
I just had a problem, line 24: if (!px || !Number.prototype.pxToEm) currentTallest = currentTallest.pxToEm(); //use ems unless px is specified…
Just commented that and worked fine.
Comment by Vinicius Borriello on 03/25 at 10:44 AM
@Vinicius Borriello: Thanks! By the way, line 24 may have been causing you trouble if you didn’t also have the pxToEm() function available. It’s a little script we developed to convert units from pixels to ems (and vice versa) when resizing components on a page, etc. More information here:
http://www.filamentgroup.com/lab/retaining_scalable_interfaces_with_pixel_to_em_conversion/
Comment by Maggie (Filament) on 03/27 at 10:52 AM
The script doesn’t work on IE7 with jquery 1.2.3. The following error is displayed:
Invalid argument.
jquery-1.2.3.min.js
Code:0
Line:24
Char:1337
Comment by Panagiotis on 03/30 at 03:56 AM
Any chance of adding a function to equalize the baselines of divs inside the container? Similar to this:
http://v3.thewatchmakerproject.com/journal/354/equalising-box-baselines-with-javascript
So you can go from this - http://jsbin.com/eguze - to this - http://jsbin.com/ofehi
Comment by Sam on 04/02 at 03:04 PM
Great plugin!
It’s beautifully crafted, I was wondering though is there a way to incorporate callback function for when all the images have completed loading? Im sorry Im a terrible code reader ...
Comment by benson on 04/12 at 10:28 PM
I’ve used similar plugin long ago
http://www.tomdeater.com/jquery/equalize_columns/
it gives the same result.
Comment by Mahbub on 04/15 at 05:52 AM
Good technique. I solved the height problem in firefox on Linux machine by this technique for my layout. It’s working fine for me. Thank You
Comment by aloy on 05/18 at 08:46 AM
Very useful..But i am still not clear about not using CSS ...
Comment by Richard on 05/21 at 06:07 AM
Wow, this is awesome!
Comment by ArthasMX on 05/22 at 07:02 PM
Hi, great script!
I also had the issue with line 24, and commenting it out worked fine. Only reason I mention is because the page states that it should default to px if the pxToEm script isn’t present, but it doesn’t work unless you comment this out.
Also, what is the symantically correct way to clear your floated divs here without having your clear div also set to the min-height?
Thanks!
Comment by Justin on 05/27 at 10:42 AM
Great script. I had hacked up my own version, but yours does the job more elegantly than mine did.
One item of note: when writing a jQuery plugin, you should use jQuery in place of the $ shortcut, as people might be using jQuery in no-conflict mode, thereby breaking your plugin.
Comment by Matt Wiebe on 05/27 at 11:11 AM
Had to rip apart a client supplied table based design today with a VERY short deadline - remembered your script and worked great!
The pxtoEm part stalled javascript so I removed as I didn’t need it.
Had one issue not mentioned before - I had a div with images in floated to the right of it’s sibling - as the text was far taller than the floated div, the non float wrapped under, so when the equalHeights script was called it sized the float div correctly to the height of the sibling, HOWEVER after the resize, logically the sibling increased in height due to no longer wrapping under the float - this resulted in mismatched heights again.
Made a very small change to the script to recursively call until a match was 100% certain:
$.fn.equalHeights = function(px,matched) {
$(this).each(function(){
var currentTallest = 0;
$(this).children().each(function(i){
if ($(this).height() > currentTallest) { currentTallest = $(this).height(); }
});
if ($.browser.msie && $.browser.version == 6.0) { $(this).children().css({’height’: currentTallest}); }
$(this).children().css({’min-height’: currentTallest});
$(this).children().each(function(i){
if ($(this).height() > currentTallest) matched=false;
else matched=true;
});
if (matched!=true) $(this).equalHeights(true,false);
else return this;
});
};
it’s called with a further ‘match’ parameter, initially set to false, ie//
$(function(){ $(’#container’).equalHeights(true,false); });
Comment by Dan Woodroffe on 06/01 at 04:19 PM
hi there,
thanks for such a useful script. it works perfectly with my firefox browser but for some reason its not working with IE7. i am using 3 column div lay out…
thanks
-jubair
Comment by jubair on 06/02 at 04:03 PM
RE: Nate on 02/09
Adding height and width attributes to your image tags will help browsers render the page quicker as well as prevent content shifts as the page loads. May help with your equal columns, too.
Comment by benxamin on 06/04 at 08:37 AM
This is explained lucidly and it worth giving a try. And thanks for sharing the same!
Comment by Bell on 06/05 at 12:56 AM
So this doesn’t seem to work for me. When I inspect in Firebug the two container heights are being set equally (which is good). However when I show/hide content within one of the containers, the min-height does not change for either container and thus results in unequal heights.
HTML
<div id="promoGiftSection">
<div id="promoCodeSection">
Sorry! This promotion requires a membership
<input name="promoCode" type="text" id="promoCode" />
<input name="go" type="button" />
</div>
<div id="giftCardSection">
<h4 class="baseCopyEm">Gift Card or eCertificate</h4>
<input name="giftCard" type="checkbox" id="gcFieldsToggle" />
<label for="gcFieldsToggle" class="inline">I have a Gift card</label>
<ul id="gcFields">
<li>
<label for="giftCardNo">* Card Number</label>
<input name="giftCardNo" type="text" id="giftCardNo" />
</li>
<li>
<label for="giftCardPIN">* PIN</label>
<input name="giftCardPIN" type="text" id="giftCardPIN" />
</li>
<li class="submit">
<input name="go" type="button" />
</li>
</ul>
</div>
</div>
JS
$("#promoGiftSection").equalHeights(true);
Comment by Dylan MacDonald on 06/10 at 06:28 PM
A question to the filament group:
Is there any way to combine this script with CurvyCorners ( http://www.curvycorners.net/ )? I cant get it to work :(
Comment by Samuel Milton on 06/11 at 05:38 AM
Nate, that’s a clever hack for handling columns withs images. thanks…
Comment by maheswaran krishnan on 06/21 at 08:22 PM
do you want the really shortcut ? use class :D
<div id=’div1’ class=’sameClass’></div>
<div id=’div1’ class=’sameClass’></div>
<div id=’div1’ class=’sameClass’></div>
<div id=’div1’ class=’sameClass’></div>
<div id=’div1’ class=’sameClass’></div>
<div id=’div1’ class=’sameClass’></div>
.sameClass{
height: 300px;
}
done !!!
Comment by adwin on 06/28 at 09:04 PM
Thanks for sharing, looked long back today got a chance to use this! works exactly as required!! thanks again!!
Comment by Vipul Limbachiya on 07/07 at 07:22 AM
Nice tutorial!
I´ve been using CSS in my projetcs and this script above will be extremelly useful.
Thanks a lot!
Comment by arquivo deslizante on 07/12 at 07:17 PM
I used CSS solution to get equal heights for solid layouts in my drupal themes, preferred matthew’s holy grail technique rather than alisapart. Yet this sort of javascript’s solution may also come in handy with simple page elements or blocks, especially when I have to dynamically generate blocks, and the blocks have to be flexible enough that may exist whether they are one, two, three, .. etc column blocks in various pages. Thanks for the tut.
Comment by gaus surahman on 07/14 at 02:34 AM
Any idea why I would have to refresh the page for the change to take place?
When the page originally loads I see the unequal columns. But, once I hit refresh the columns equalize.
I am using FF3 on a mac.
Comment by Benjamin on 08/05 at 10:37 PM
Very Nice, Thank you for your sharing .
Cheers
Elijah
Comment by Dindigul on 08/25 at 06:01 AM
displaying “G.replace not a function” error.
i got prototype.js and jquery-1.3.2.min.js in same page.
i did replace $ with jQuery and include jQuery.noConflict().
but still showing above error.
Thanks.
rvs
Comment by rvs on 09/23 at 11:45 AM
提供下载吗?
Comment by lgh on 09/24 at 10:36 PM
http://www.fusionteam.co.uk/blog/2009/09/28/setting-equal-heights-using-jquery/
Comment by fusionteam on 09/28 at 03:22 AM
The information provided is very useful to harness our skills further. This really provides a good understanding and simple explanation of the material.
Comment by Payday money on 10/05 at 05:22 AM
biligiler cok iyi ya beendıms aolun
Comment by sikiş izle on 10/26 at 03:31 PM
tanks for the information, its much usefull. i will comment in my blog about your post.
tank you.
Comment by boutique arte angels on 11/14 at 04:08 AM
vety nice taks admin
Comment by sikiş on 11/18 at 09:19 AM
i use the same method but i fnd some throble with ie8 some body know any solution? (my tables don’t show).
Comment by criacao-sites on 11/22 at 01:29 PM
I like this solution, it can save some annoying div nesting, but i use css instead(thanks to Mathew James Taylor for that), because you’ll usually have dinamyc content and when objects move around the heights change. With css all columns preserve the same height.
Comment by Amos on 11/25 at 04:36 PM
hi, this solution that all i search some many time, tank you for your publication.
Comment by travel airfare hotel on 11/28 at 04:02 AM
Hi guys,
I have a little problem with this plugin.
In my div #equalize, I have another div with class minifiche and another one with css clear: both. EqualHeight works perfectly with minifiche but it give the same height to the div with clear.
How can i resolve this ? Thx.
Comment by Kcin on 12/02 at 07:05 AM
After many try, I manage to do what I want.
$(’#clearer’).css({’height’: 0 + ‘px’});
$(’#clearer’).css({’min-height’: 0 + ‘px’});
Very simple :D
Comment by Kcin on 12/02 at 07:58 AM
Great plugin! It works fine in Firefox and IE, but in Chrome it’s not working at all here
Comment by Jorre on 12/31 at 06:58 AM
Hi this is nice code, but when I add any paragraph in B box then on IE 6 or IE7, I will get 2 px more height. Compare A and C boxes. Please give me any solution.
Thanks
Amol Kshirsagar
Comment by Amol on 01/01 at 11:59 PM