5

I've used Chris Coyiers Equal Height Blocks in Rows jQuery script many times before, and it's always worked great.

That being said, I've always developed websites built for a specific resolution. This time around I'm developing a responsive website and main container has a fluid width. As such, I've encountered an issue and can't find a solution to my problem.

In his post, Chris says this about fluid widths:

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.

Note that he's specifically referencing a fluid width container. In my case it's not just the container, but the child elements that also have fluid widths. The very blocks that I need to equalize are the same blocks have fluid widths and adjust to the resolution of the window/container.

I believe that because the element's 'originalHeight' changes with the container size, the script will no longer work properly.

My speculation may be wrong, but one way or another this isn't working with fluid width elements. Looking for some help!

Of course, here's a jsfiddle I created to demonstrate the problem. Just resize the width of the window and you'll notice the heights of the containers do not update.

I'm running the following code to call the function on the window resize event:

$(window).resize(function() {
    columnConform();
});

Thanks in advance

norsewulf
  • 511
  • 6
  • 21
  • 1
    This is such a common issue in every site I build these days. I can't believe there isn't a consensus on this as of 2014. Excited about @Popnoodles 's answer! http://codepen.io/sheriffderek/pen/nJwqH/ – sheriffderek Jun 23 '14 at 06:09
  • From @Popnoodles ' answer http://stackoverflow.com/a/14167351/1010918 I removed the rows bit in the JS to have equal heights on all columns. http://jsfiddle.net/JYnCb/81/ I throttle the resize with http://www.paulirish.com/2009/throttled-smartresize-jquery-event-handler/ – lowtechsun Feb 16 '16 at 01:07
  • @lowtechsun that's what the first (small) block of code in my answer does. You have way too much code for what you need. Here: http://jsfiddle.net/JYnCb/82/ – Popnoodles Feb 16 '16 at 12:42

2 Answers2

6

You're only equalizing the heights of columns that exist on one row so you could replace all that JS with this

$.fn.extend({
    equalHeights: function(options){
      var ah=(Math.max.apply(null, $(this).map(function(){ return $(this).height(); }).get()));
      if (typeof ah=='number') $(this).height(ah);
    }
 });

and execute like so

$(document).ready(function() {
    $(window).resize(function() {
        $('.equalize').equalHeights();
    }).trigger('resize'); // don't write code twice just to execute on page load - trigger the event you just bound to instead

})

But if you want to do it per pseudo row working demo

<ul class="eqme">
  <li><h1>One</h1></li>
  <li><h2>Two</h2></li>
  ....
</ul>

JS

$.fn.extend({
equalHeights: function(){
    var top=0;
    var classname=('equalHeights'+Math.random()).replace('.','');
    $(this).each(function(){
      if ($(this).is(':visible')){
        var thistop=$(this).offset().top;
        if (thistop>top) {
            $('.'+classname).removeClass(classname); 
            top=thistop;
        }
        $(this).addClass(classname);
        $(this).height('auto');
        var h=(Math.max.apply(null, $('.'+classname).map(function(){ return $(this).outerHeight(); }).get()));
        $('.'+classname).height(h);
      }
    }).removeClass(classname); 
}       

});

$(function(){
  $(window).resize(function(){
    $('.eqme li').equalHeights();
  }).trigger('resize');
});

This will even take care of many elements that are the children of the same parent even if they break row, e.g. a ul containing 100 li elements when only 10 fit on one row, or when the li width is static but the page width is not, on resize they'll clear onto new lines when the window is shrunk (or float back to the one above if expanding the width) then their heights will fix to suit the tallest per pseudo row.

Notes:

Feedback suggests that outerHeight() works better than .height() in some situations.

In production, within the window.resize function, I added a clearTimeout and setTimeout of 100ms before reacting to the change of size to prevent slowdown when resizing by dragging a corner.

Popnoodles
  • 28,090
  • 2
  • 45
  • 53
  • So the top code is if I only plan on equalizing this one single row of elements? And if I plan on doing it throughout my site in various other rows, I should use the bottom suggestion? – norsewulf Jan 05 '13 at 01:01
  • Awesome thanks! I haven't implemented it yet but it looks to be correct. Cheers! – norsewulf Jan 07 '13 at 17:19
  • I'm experiencing a bug. Check out [this fork](http://jsfiddle.net/norsewulf/Cnknr/) of your jsfiddle. As you can see I've given all three elements that were previously on one 100% widths, obviously causing them to stack on top of each other. They look fine when you land on the page, but if you start resizing the window to various widths, you'll notice that sometimes they 'think' they're on the same row and they all equalize to the same height. Wouldn't be an issue if it was only on resize, but in my real world project I'm even experiencing this on the original page load. Any ideas? – norsewulf Jan 10 '13 at 06:26
  • Oh also, I don't know if this has anything to do with it but I've modified the script to use outerHeight() instead of height(). – norsewulf Jan 10 '13 at 06:27
  • Sorry one last thing I noticed! When resizing windows the class .equalHeightsGroup_'+j can really start stacking up quickly. Is there anything that can be done to resolve this? Is it a cause for concern? – norsewulf Jan 10 '13 at 06:33
  • Also note, if you have images on the page use window load not document ready – Popnoodles Jan 10 '13 at 07:01
  • Ahhhh I found my problem! It has to do with the child elements of the container. When they are position:absolute, there are some issues. See this [fork of your jsfiddle](http://jsfiddle.net/norsewulf/Cc2BW/). Also, is this correct if I want to run the equalize script on window load: `$(window).resize(function() { $('.equalize').equalHeights(); }).trigger('resize');` – norsewulf Jan 10 '13 at 07:19
  • While you were writing that comment I've rewritten it from scratch using the map function, and going through each element resizing that row until it hits a new row. The problem it was having was that it didn't know of resizing one element would push another onto the next row or make one fall back. I've tested this in a real situation and pretty confident it's 100% now. http://jsfiddle.net/JYnCb/ No stacking of classes either. – Popnoodles Jan 10 '13 at 07:51
  • Thanks for taking the time to improve the script! I really appreciate it. However when I test this with absolutely positioned children (as I'm using in my layout), the same problem exists. I've mimicked my layout in [this jsfiddle](http://jsfiddle.net/norsewulf/Cc2BW/) to demonstrate. – norsewulf Jan 10 '13 at 17:27
  • I'm confused. You've given two of them a width of 48% and one a width of 100%. How do you expect it not to appear on the next line? – Popnoodles Jan 10 '13 at 18:46
  • I do want it to appear on the next line. The layout is correct. The script however is applying the tallest height to the other two elements on the line above. The two elements with 48% width are getting the height of the element with 100% width on the row below. Does that make sense? – norsewulf Jan 10 '13 at 19:41
  • oh right. just checked. it believes they are all on the same row because they are positioned absolutely in the same place. why are they absolutely positioned? – Popnoodles Jan 10 '13 at 20:38
  • The example I supplied just has some

    tags, but in my actual project I have to position absolutely for vertical centering and some other reasons. The thing is, the parent containers themselves are not absolute, they're relative. Even if you give a min-height to the parent containers, they problem will still exist. I guess I don't really understand what you mean when you say 'they are positioned absolutely in the same place'.

    – norsewulf Jan 10 '13 at 21:31
  • Add in a `console.log(thistop);`. As it iterates it's finding the offset.top of each. When it sees a change in offset.top it recognises that as a new row, leaves the ones it's done and works along the row until it hits another change in offset.top. Remove the JS and your divs are all at the same top position on the page, so it thinks they are all on one row. If you want the third element to behave differently why include it in that call? – Popnoodles Jan 10 '13 at 23:51
  • Okay I see the problem now. I'm going to look into coding my markup differently. Apparently there is a technique for [faux absolute positioning](http://www.alistapart.com/articles/fauxabsolutepositioning/) that could solve my problem as it will keep the element from leaving the flow of the page. I'll follow up after I give it a shot. – norsewulf Jan 11 '13 at 01:14
  • Alright I've found jQuery solution for vertical centering, which will make it far easier to avoid using absolute positioning. In turn, your script seems to be working great! Thanks for all the help!! – norsewulf Jan 11 '13 at 05:27
  • Sorry to bother you again popnoodles. This isn't a bug, but I found an issue that could cause some headaches. I have an element that has the style `display:none;`, however because it is on the same row and has my `.equalize` class on it (for display later, via media-queries), it's affecting the other elements even when it's not visible. Could the script be modified to account for this? – norsewulf Jan 14 '13 at 19:50
  • Added `if ($(this).is(':visible'))` this is something you should have been about to google for and put in the right place – Popnoodles Jan 14 '13 at 20:57
  • I'm aware of how to use `if ($(this).is(':visible'))` and played around a bit with it. I just couldn't figure out exactly where to place the statement. Thanks for updating it! – norsewulf Jan 14 '13 at 22:05
  • Hi again popnoodles. I've asked a question in regards to your script and how to get it working while using `outerHeight()` for jQuery < 1.8.0. The problem is there is no setter for `outerHeight()` before jQuery 1.8.0. Just thought I'd see if you have any ideas since you came up with this awesome script. You can see my question [here](http://stackoverflow.com/questions/14635425/jquery-outerheight-bug-doesnt-work-properly-below-ver-1-8-3) – norsewulf Feb 04 '13 at 19:55
  • outerHeight was added in 1.2.6 – Popnoodles Feb 04 '13 at 23:09
  • True, but not the setter was only added after 1.8.x. I was able to get it to work by integrating my own `outerHeight()` helper. I posted about this in the new question. Anyways thanks for your input. – norsewulf Feb 04 '13 at 23:14
  • Hi Popnoodles, I ran into a bug with your script and I've updated your jsfiddle to demonstrate. It seems the height won't work properly on elements with top/bottom padding or margins. You [test it here](http://jsfiddle.net/JYnCb/55/) – norsewulf Feb 15 '13 at 18:35
  • Ok thanks. I always use a wrapper and put any padding on inner elements. It's a lot easier to work that way. – Popnoodles Feb 15 '13 at 18:43
  • Fair enough.. it's been enough headaches trying to get it working with padding and/or margins. I'll just stick another wrapper inside and be done with it. Thanks – norsewulf Feb 15 '13 at 19:05
  • I don't know what the state of this is... but I used it here: http://codepen.io/sheriffderek/pen/nJwqH/ ( switched height(); to outerHeight(); ) and it's working like a charm so far. – sheriffderek Jun 23 '14 at 06:00
  • PS: @Popnoodles, you are awesome. – sheriffderek Jun 23 '14 at 06:14
  • @sheriffderek i've added a note because you're the second person to use `outerHeight()`. I should really add it to the extension. `$('.equalize').equalHeights({useOuterHeight: true});` – Popnoodles Jun 23 '14 at 12:24
2

Assuming I understood your question, this code should work:

$(document).ready(function() {

    $(window).on('resize', function() {
        $('.equalize').css('height', highest($('p')));
    }).trigger('resize');

});

function highest(el) {
    var highest = 0;
    el.each(function() {
        var height = $(this).height();
        if(height > highest) {
          highest = height;
        }
    });
    return highest;   
}

Fiddle

JimmyRare
  • 3,958
  • 2
  • 20
  • 23