There are plenty of solutions for equal height columns using jQuery (like matchHeight.js). The basic idea is to measure all of their heights and then set all their heights to that of the tallest one.
But what if there is multiple rows of blocks and you only want to increase the heights to match in the current row? This could happen from having different sized thumbnails or different amounts of text in each box.
Stephen Akins posted a neat idea on how to solve this. The idea is largely the same, test the heights of divs and set them to the tallest, but before it does that it measures the top position. From that, it is able to tell which divs belong to which row and only affect divs within that row.
Here is the whole thing. I just tweaked it a little from Stephen’s original to make it a bit more efficient.
// IIFE
(function() {
var currentTallest = 0,
currentRowStart = 0,
rowDivs = new Array();
function setConformingHeight(el, newHeight) {
// set the height to something new, but remember the original height in case things change
el.data(“originalHeight”, (el.data(“originalHeight”) == undefined) ? (el.height()) : (el.data(“originalHeight”)));
el.height(newHeight);
}
function getOriginalHeight(el) {
// if the height has changed, send the originalHeight
return (el.data(“originalHeight”) == undefined) ? (el.height()) : (el.data(“originalHeight”));
}
function columnConform() {
// find the tallest DIV in the row, and set the heights of all of the DIVs to match it.
$(‘.blocks’).each(function() {
// “caching”
var $el = $(this);
var topPosition = $el.position().top;
if (currentRowStart != topPosition) {
// we just came to a new row. Set all the heights on the completed row
for(currentDiv = 0 ; currentDiv < rowDivs.length ; currentDiv++) setConformingHeight(rowDivs[currentDiv], currentTallest);
// set the variables for the new row
rowDivs.length = 0; // empty the array
currentRowStart = topPosition;
currentTallest = getOriginalHeight($el);
rowDivs.push($el);
} else {
// another div on the current row. Add it to the list and check if it's taller
rowDivs.push($el);
currentTallest = (currentTallest < getOriginalHeight($el)) ? (getOriginalHeight($el)) : (currentTallest);
}
});
// do the last row
for (currentDiv = 0 ; currentDiv < rowDivs.length ; currentDiv++) {
setConformingHeight(rowDivs[currentDiv], currentTallest);
}
}
// If the content might change... probably debounce this and run it.
// $(window).resize(function() {
// columnConform();
// });
// DOM Ready
// You might also want to wait until window.onload if images are the things that are unequalizing the blocks
$(function() {
columnConform();
});
})();
$('.blocks')
would be changed to use whatever CSS selector you need to equalize. Or have the function accept a selector as a param or something.
What About Fluid Width?
One of the main points of not using a table for this is that floated divs can rearrange themselves based on the available horizontal space, which is nice for fluid width. We can adjust the above code to deal with that as well. Basically the first time this is run we’ll measure the height of each block and remember it with jQuery’s data() method. Then when the window is resized, run the code again but use the original size to adjust the rows, not the new size.
The demo for this is fluid width. If you want that code, just view source and snag it:
Wish it didn’t have the jQuery dependency?
- Ben Howdle: Easy Peasy Equal Heights
- Vitalii Skorokhod: javascript equal height blocks
\
Thank you! I’ll try it on my sites
Oh, Chris… when did you start mixing presentation with Javascript?
Oh, BFRED.IT… I post solutions like this when they are clever and solve real world problems. It kinda sounds like you take issue with this, so please enlighten.
Perhaps using display: table-cell; http://bfred.it/b/full-height.html
I just made it, I haven’t tested it outside webkit, and it may not be elegant, but it’s supposed to work even when JS is off.
I’m personally not a fan of
display: table-cell;
. If going that route, there is no difference between using div’s and making them behave like tables and just using actual tables (which would have deeper browser support).That said, if the type of data being displayed is tabular in nature, tables are a way better solution than using JavaScript.
Sure there is, tables are tables, divs are generic containers. div’s with `table-cell` are seen by machines as containers, not tables
Sorry for how I started this conversation, I always thought of this as a “solved problem”
For every person that volunteer valuable tips like this, there’s always the “yeah, but..” guy that can throw an untested version together on their own site. :)
Regardless, cheers and thanks for sharing this – I’m envisioning many opportunities to use equal heights for multiple columns in the future.
If the argument is increase semantics of divs over table cells, then yep, if it’s non-tabular data there is a benefit there.
The original problem here was “non JavaScript users are left out” which is true, but if you go display table-cell, even more people are left out.
IE7 = 6% ish
no-JS = 2% ish
(of course it depends on the site)
Good point, then we could add the above JS solution conditionally on
lte IE7
to make everyone happy :)I’d be careful with that, css on anything that has to do with tables–even tables generated with css–can go badly very quickly. Try making those cells position:relative with absolutely positioned elements inside and see what happens. (The css 2.1 spec specifically leaves that undefined :( )
I remember there was a fix for that, something like using a div inside the “table cell” and position what’s inside. But then, yeah, it depends on what you have to do. If you just have to make text columns it will work fine. As a matter of fact it could be used as a fallback for the browsers which don’t support CSS3’s text columns.
I’m not sure the div fix will work here, it won’t be the height of the cell and you’re back to square one if you want something positioned at the bottom.
If you really have to, you’ll find a way to position absolutely in whatever specific case you have (see my updated example in the first column for a solution to that), but we’re still talking about text columns, at most you’ll need floating images in there.
Another (partial) CSS-only solution is setting a
min-height
, which works best on static websites or on controlled content (e.g. post excerpts).Keep in mind that Chris’ example won’t work on fluid layouts, if you decide to go down that route. That doesn’t mean we’re going to promote the use of the onresize event or the non-standard
expression()
to solve real world problems.If it’s to increase usability or add progressive enhancement, sure, go ahead and use that javascript, but don’t use it for layout, that’s reminiscent of the (bad part of the) early 2000’s.
Jimminy Cricket.
But the original article was *not* only talking about text columns.
I merely point out that there is a trade-off–you can use display:table-cell and have your css only solution, but you’ll lose other things you might want. If you don’t want them, by all means use display:table-cell. I point this out because I have spent far too much time trying to work around this, and other people here might run into the same problem.
And yes, I do think this script is basically aesthetic, not layout. I haven’t tried this particular one, but in concept all of the content is there, and nothing changes between js and no js except the bottoms of the blocks. Note from the example “Booo” image that the second row is all correctly top-aligned, cleared, whatever. You should already be doing that with your css, this just makes the boxes the same height so it looks a little neater.
BFRED.IT:
Doesn’t work for Liquid layouts? What do you mean? This is intended for Liquid layouts. It works for any number of DIVs on a line, so if your window has room for 10 DIVs, or 2; the DIVs on each row will be the same height (the height of the tallest DIV in each row).
If you look at my blog you will see a version that works with the onresize event so you can grab the edge of your window and try different widths and watch the DIVs flow and resize when they move from row to row.
And guys: Who cares if JS doesn’t work? The fix is purely cosmetic. People browsing the web without JS are going to be experiencing way worse issues than not having their DIVs all match in height.
Stephen, my sentiments exactly. People that say “oh, but will it work with Javascript turned off?!!??” never seem to acknowledge that 99% (just a rough figure) of the web wont’ work properly with Javascript turned off. Try turning it off and visiting your favorite sites. Heck, try turning it off and visiting THIS site. It ain’t pretty (no offense Chris).
I absolutely LOVE this script. Combined with the likes of 960.gs it’s superb.
Forgive me if this sounds rude.. but for the extreme minority of desktops, macs, notebooks and laptop users who actually surf the net, if they’re not using javascript then are they really people who are going to give us profits from using the web or at least serve us benefit from browsing our websites?
I think this is like an IE6 issue dilemma.. so many people have ceased making websites with support for IE6, not because it’s a bad browser.. its just dated.. we’re in a world of CSS3, Jquery, HTML5, webkit and not to mention IE9. It surprises me that bloggers and readers even worry about the support of JS anymore.
i can’t believe this… after three days of struggling i’ve finally found a solution for this, and just two hours after you publish this article :-))))))))))
anyways, those who interested in the subject, check this forum thread:
https://css-tricks.com/forums/discussion/10602/wrapped-content-that-stretching-two-surrounding-div-s
i’ve posted many useful links there..
Instead of
you could use Math.max:
This saves you from calculating the height twice as well.
Thanks Pete, I’ll use your suggestion in my own code.
Thanks for sharing this Chris, I love the “equalizeThoseMoFos()” part!
Seriously now, I’m not sure I’d use a javascript solution for this matter.
Most probably I’d use a trick to fake equal height for them.
how about using min-height and set it to the tallest column height.. and you’re done .
won’t work if the tallest column is generated by content ;-)
I really like this, I tried figuring out how to do it, some time ago, but It sounds quite impossible using only the max-height CSS feature. I know now that this is the best solution for equal-height columns. thanks for the idea
after three days of research i would not say “it’s the best solution”.
there are plenty of ways to achieve this effect, and one should decide depending on situation.
Depending on the design for the backgrounds of the columns, you can achieve this effect without Javascript by adding a repeat-y background to the columns’ parent element.
…which is known as “css faux columns”. :-)
cool, didn’t know it had a name :-)
Problem with that is if you want to have some space between rows of divs, then you need to give each row a container, which would provide many simpler solutions.
That’s true. But in most situations I think adding a little non-semantic markup is a lesser evil than relying on JavaScript for layout.
Nice setup. I have made a plugin that does this, however it does not work with this “row idea”. Mine would be handled by rows itself.
Definitely a nice idea about the top position.
Here is my plugin (I do have an update to give more functionality that will be coming soon) https://github.com/madmike1029/matchheights
Great stuff :)
If only I saw this a month ago.
Oh yeah! This is great. The thought of checking the top-position is something I never figured out.
And by the way I really like your naming, eg.
equalizeThoseMoFos();
. Like!Hey Chris, great post! This will help a lot of people trying to find a solution to getting equal height columns but are struggling to find the answer. I look forward to your next article!
Hey Chris.. Could not understand this post.. It can be done by taking some or also..
Hey Chris,
I think it’s a great, simple/quick fix to an annoying problem. Thanks for sharing.
its not just html and css plus there’s quite some code – its good (even great) if you have a lot of dynamicly generated blocks.
But if you use only a couple of rows (lets say displaying 5-10 latest news on a site) its much ‘cheaper’ to use tables (no server/client code – plus less of it)
Yea yea i know u cant eat ice cream from a salad bowl … but hey it turns out u can ;)
i love how you make things less scary. i have struggled with jquery for a while but you make it so much easier. you are a legend chris.
the boxes being different heights is really annoying!
Why not just use a table?
exactly!!!!!
Because you can have a variable number of columns. You can’t do that with a table.
Looks like a(curiously repeated) typo introduced an unintentional global:
“topPostion”
Did you Lint?
Also “currentDiv” is a global.
“new Array()” and “.length = 0” are a bit w3schools, why not just “[]”?
What a cool concept it helps me …
cause i wnt to build a website execution just like this one
http://linesforme.blogspot.com
I’m having some trouble getting this to work on a standard WordPress install.
Let me clarify – if I call jQuery manually from the google library it works.
Trying to enqueue jQuery or rely on the already called jQuery from wp_head() does not seem to be working.
Anyone have any ideas? I’m using Chris’s other article on enqueuing as a guide, but thus far no luck. [http://digwp.com/2009/06/including-jquery-in-wordpress-the-right-way/]
Great article. I seem to run into this problem alot and feel dirty everytime I have to use a table for non-tabular data.
This is a really good execution, nicely done!
I made a similar function last year sometime to do just this but you had to specify the number of elements per row.
It worked great for that instance but a nightmare to reuse. Never thought of checking top position – will definitely reuse this technique!
Thanks :)
I actually found a method to do exactly this using just CSS. Yes, you can.
http://www.isegura.es/blog/two-columns-same-height-using-css-and-no-javascript-yes-you-can
Just because you can, doesn’t mean you should.
I had a version of this where i had to set the number of columns as an argument, but this is much nicer :)
I made a jQuery plugin out of this to maintain chaining. And also minified it.
Hope you don’t mind :) Feel free to fork it or whatever.
https://github.com/krishandley/jQuery-Equal-Height-Blocks
You plugin doesn’t actually maintain the chain. You need to return the objects : http://docs.jquery.com/Plugins/Authoring#Maintaining_Chainability
Should the following code be put outside of the “each” loop?
// do the last row
for (currentDiv = 0 ; currentDiv < rowDivs.length ; currentDiv++) {
rowDivs[currentDiv].height(currentTallest);
}
That way you can save lots of unnecessary height setting…
Yeah, I was looking at that too. First, that code is in their twice. The first instance, after the if (currentRowStart != topPostion) isn’t neccessary. The second instance (toward the bottom after the // do the last row) runs once for each item in the jQuery collection and will take care of . This is inefficient because you end up setting the heights on a row once for each column in that row.
Ideally, you would set the heights just once the each row after the each(). I’m sure the performance hit is pretty small for a just a handful of rows and columns.
Another problem, is that if you have top or bottom padding in just one of the items, then the heights are measured accurately. Just something to keep aware of. Tom Deater’s equalize columns plugin solves the padding problem: http://tomdeater.com/jquery/equalize_columns/
However, that plugin doesn’t provide a way to equalize columns by row–which this code does.
Maan! Where was this a month ago. Tried fixing these damn columns for hours. Dubweiser.net
Use “outerHeight” instead to get the correct dimensions for elements with padding/margin.
http://api.jquery.com/outerHeight/
I was looking for it. Thank you very much.
I tested so many jquery equal height codes and plugins, this one is absolutely the best one… I wonder if it is possible to make a plugin of it?
Awesome script. It doesn’t work accurately when
box-sizing: border-box;
is active on.blocks
. My quick fix was to add the total vertical padding to thecurrentTallest
when applied to the blocks. However, I would love a solution that doesn’t require me to set padding at all. Especially since I loveborder-box
.For those who have a div with padding change:
currentTallest = Math.max(currentTallest, $el.height());
to
currentTallest = Math.max(currentTallest, $el.outerHeight());
Thanks for the script.
Thanks Chris !
So I’m using the fluid version from Chris’s demo but there is a slight issue with this and I can’t seem to solve it.
The Problem
This solution almost works with fluid width layouts and resets height when top positions changes but it resets it to the original height of the current tallest element in the row when the page first loaded. This is an issue because the height of the tallest element might have changed since this page first loaded because of the use of relative units like ems or because of word wrapping with paragraphs.
Proposed Solution
The solution would be to have the row’s elements’ height being set to the tallest element’s current height, not the original height.
Have you tried this with images in the boxes? If there are many boxes with images or images with large file sizes the JavaScript will run before the images are loaded. When every image is loaded some boxes are cropped.
Try it on window load.
I do so. But I wonder do I have to wait for the whole site to load? If I select a div which is around the image .load() doesn’t seem to work.
Here is something similar I put together:
https://github.com/Sam152/Javascript-Grids
exactly what i was looking for! thanks so much sam, chris & Stephen!
It’s great.Thank you so much!!! :)
I’ve got a modified version of this working great, except for when the browser window is reduced in width.
I have fluid widths, heights on the items in the rows which reduce in width with the browser. There are mediaquery breakpoints which change the number of items per row. The problem is when making the browser smaller the items get smaller until they hit the breakpoints and then the number of items per row decreases and the item get bigger again to fit the extra space. The script is taking the the smaller sizes before the breakpoint adjustments so set height are off and the row overlap.
Does anyone have an idea how to solve this?
I needed a robust version of this technique, that handled things like fluid and responsive layouts, rows, delayed images, mixed padding, margin, box-sizing, arbitrary DOM ordering etc.
So here’s my go at a plugin for equal heights that solves the practical issues I had, if it helps anyone:
https://github.com/liabru/jquery-match-height
Here’s my version of doing this with no JS:
http://jsfiddle.net/shay/3P7Nk/
Although I think both flexbox and display:table are common enough to be seen as the go-to solutions for something like this.
@Liabru – Nice script definitely handles equal heights better where there’s issues with padding, margin & box-sizing etc. I’ve tested it on a site I’m working one and the heights of the div I target don’t collapse on mobile devices now which they did before.
Other scripts were great too it has to be said! Thank you to all of you for developing these equal height scripts and continuously improving them!
@Shay Acrich – I like the idea behind your solution. Could you not just use a pseudo elements instead of the prefix-elements you’ve used to get the heights to match. Otherwise we have a random div in there that’s only for styling.
@Rich Dykins, excellent idea with the pseudo elements!
Here’s the updated implementation, again, JS free: http://jsfiddle.net/Ac5qG/2/
I’ve had a stab at making this into a jQuery plugin http://codekipple.com/conformity/
This is still a useful snippet but could use some corrections:
correct typo:
topPostion
totopPosition
use
.offset().top
, instead of.position().top
– more likely to actually work as intended and correctly discern between rows, particularly in responsive layouts. Using.position()
can fail because you haveposition:relative
set on your row (elements), or some parent of.use
[]
instead ofnew Array()
–new Array()
comes with a bunch of pitfalls and no advantages over[]
and it’s a good idea to discourage JS devs from using it at all.as a couple of commenters above have noted, it’s quite common to want to use this in pages where you have images inside your blocks, so a note to use this on
$(window).load( ... )
would be helpful.Thanks!
These are some useful tips, pull requests to my plugin encouraged!
https://github.com/codekipple/conformity
I thought this was working on my new website, but this is a case where it doesn’t and I can’t for the life of me figure out why it fails to align the rows.
Url is http://www.drapertools.org.ukcatprods.pgm?name=Spirit%20Levels&cat=00007875
Select to view “All Products” and Gallery View.
About halfway down the page the alignment blows up.
Note: It seems to work ok with Chrome and Safari, but not with Firefox, unless it’s the latest version of Firefox?
I tried your revised code, couldn’t get that to work, but I see your demo is still using Stephen’s code.
Thanks for any help on this.