IE8 <div> Height Changing

posted Oct 2, 2012, 1:46 PM by Tyler Akins   [ updated Oct 2, 2012, 2:48 PM ]
This was a problem that stumped me for quite some time.  I'm working to create a pagination plugin where you have a single parent <div class="results"> that contains several tile <div class="tile"> elements.  Basically, the structure looks a little like this:

<div class="results" style="overflow: hidden; position: relative">
    <div class="resultsWrapper">
        <div class="tile">Result # 1</div>
        <div class="tile">Result # 2</div>
        <div class="tile">Result # 100</div>

I add some styles to div.results to make it only show a few tiles at a time.  Because the tiles can have a variable height, I use jQuery to calculate this:

// Error detection and bounds checking removed for clarity
var page = 3;  // zero-based indexing
var perPage = 5;
var children = $('div.results').children().children();  // Get the tiles
var firstChildTop = Math.floor(children.get(0).position().top);
var firstVisibleTop = Math.floor(children.get(page * perPage).position().top);
var lastVisibleBottom = Math.floor(children.get((page + 1) * perPage).position().top);
// Show the divs on this page
$('div.results').animate({ height: lastVisibleBottom - firstVisibleTop });
$('div.results').children().animate({ marginTop: firstVisibleTop - firstChildTop });

Remember, this is just an example to help illustrate what I am trying to do.  You'll need quite a bit more code to make a working pager plugin for jQuery.  Anyway, so this will appear to the browser that there's a sliding series of div.tile elements moving to the "page" that you are on.  With the "overflow: hidden" and the negative margin, this acts like a little window seeing just a portion of the larger div.resultsWrapper that is sliding around to show just what we need.

Except in IE8.  It's also not the case sometimes in IE9 when rendering in IE8 mode, but only sometimes.

The problem boils down to the heights of the elements.  When IE8 slides the div.resultsWrapper up, the div.tile elements forget their heights.  It's crazy, but you could have some JavaScript like this to show the heights:

var h = 'Heights: ';
$('div.tile').each(function () {
    h += ' ' + $(this).height();

You'll see output like this when at the top of the list:

Heights:  212 197 197 202 212 207 ...

Now use a little jQuery magic to scroll down by setting a negative margin-top CSS property on div.resultsWrapper.  Let's say you scrolled down so just a little of the bottom of the fourth element is shown.  Move your mouse over the div.results element.  Now, run that JavaScript again that shows the heights.  I was seeing this:

Heights:  47 47 47 768 212 207 ...

The height of the first three shrunk to just the padding I had on div.tile and the fourth tile strangely sucked up most (but not exactly all) of the height that was missing.  You can move back to the top and the content is messed up until you mouse over div.results.  I set a global breakpoint and no JavaScript runs when I mouse over div.results, yet that's still when the heights changed.  After much trial and error, I found that the contents of the tiles were to blame.  Here's closer to what my tiles looked like, and I bet you'll start to get a feel for where the problem lies.

<div class="tile">
    <div class="productImage" style="float: left"><img src="..."></div>
    <div class="productDescription" style="float: left">This is result #1</div>
    <div class="clear" style="clear: both"></div>

My divs used "float: left" to position them inside the div.tile element properly.  This works well in all browsers and looks great even in IE8 and IE7 (I have no need to go lower).  The only browser that chokes is IE8.  It must do something when the div.tile elements are above the visible area and it just doesn't keep them loaded or positioned properly.  This feels a lot like another type of "peekaboo bug" that has plagued IE with floats ever since they were introduced in that browser.

The fix:  Do not use float.  Yep, I tried several variations, but nothing ever worked with dynamically sizing content and floats.  In the end "float: left" was replaced with "display: inline-block" and it again looks perfect in all browsers.