Frayt
Frayt

Reputation: 1223

Using % width instead of px ruins alignment of divs

I have two divs on my page, the first one is a small one used to navigate, and the second one is the main content. The desired result is that they will line up side by side.

Because the main content takes up a lot of horizontal space, I want it to fill 75% of it's container, so that it will get larger or smaller depending on the screen width. Obviously this is a simple width:75% in CSS but when I do this, the main content div refuses to align to the right of the navigation div. However, if the div at 75% was taking up 400px and I changed the width:75% to width:450px then the divs align exactly how I want them to, despite being the same size. When I look at the box model for the main content div I can see the content is 75% as it is supposed to be, but there is a margin on the right of it which takes up the rest of the container. I have tried setting it to margin-right: 0px; but it doesn't change at all and the margin is still there.

The only CSS I have defined for this is:

#mainArea_pnlErrorLog {
 width: 50%;
 position: relative;
 margin-right:0px;
}

Setting position:absolute; fixes the problem however it creates a new one, because the container will not vertically expand to fit the main content and it goes outside of the container

Here's a screenshot of the web page to better illustrate the problem: !1

Upvotes: 2

Views: 153

Answers (1)

Ilmari Karonen
Ilmari Karonen

Reputation: 50328

You problem is that, in your HTML code, you've got two nested <div>s styled with display: inline-block:

<div id="mainArea_pnlControls">
     <div id="mainArea_pnlErrorLog">
         ...lots of content here...
    </div>
</div>

#wrapper {
    width: 420px;  /* fix surrounding element width */
    padding: 10px;
    background: #ddd;  /* gray */
}
#controlsList {
    display:inline-block;
    width:145px;
    height:50px;
    background: #faa;  /* red */
}
#mainArea_pnlControls {
    display:inline-block;
    vertical-align:top;
    background: #afa;  /* green */
}
#mainArea_pnlErrorLog {
    width: 50%;  /* set to 200px to see intended rendering */
    position: relative;
    background: #aaf;  /* blue */
}
<div id="wrapper">
    <div id="controlsList">
         SIDEBAR
    </div>
    <div id="mainArea_pnlControls">
         <div id="mainArea_pnlErrorLog">
CONTENT CONTENT CONTENT CONTENT CONTENT CONTENT CONTENT CONTENT CONTENT CONTENT
        </div>
    </div>
</div>

The outer one, mainArea_pnlControls, has no explicit width defined in the CSS, so it expands as necessary to accommodate its content. When you give the inner one a width: 50%, you tell the layout engine to make the inner <div> half as wide as the outer one.

This is kind of confusing for the layout engine. On one hand, by using display: inline-block for the outer div, and not specifying an explicit height, you've implicitly told the browser to make it as wide as the inner div inside it. On the other hand, by specifying width: 50% for the inner div, you told the browser to make it only half as wide as the outer div.

Obviously, these requirement conflict (at least unless both divs have zero width), so one of them has to fail.

Firefox, at least, chooses to fail the implicit requirement of equal width, and instead to make the inner div half as wide as the outer one, to honor the explicit width: 50% requirement. But that still leaves open the question of exactly how wide should the outer div be in the first place. Since it's an inline block, it shouldn't be wider than the block enclosing it (unless it's forced to overflow its container), but the only lower bound on its width is that it needs to accommodate its content. But in this case, the content will always shrink to be narrower than the outer block, so... there's just no meaningful lower bound available there.

Apparently, what Firefox ends up doing is (something like) this:

  1. Figuring out how wide the content of the inner div would be, if it could expand freely to be as wide as it wants.

  2. Set the width of the outer div to be the minimum of the width the content "wants" and the width of the enclosing block.

  3. Set the width of the inner div to be 50% of that.

  4. Render the content of the inner div at that width (which guarantees that it will either wrap or overflow).

You can see the effect clearly if you make the content narrower, so that it would actually fit cleanly on one line: it will still wrap, since the inner div ends up being exactly half as wide as it needs to be to accommodate it, as in the snippet below:

#outer {
    display:inline-block;
    background: #afa;  /* green */
}
#inner {
    width: 50%;
    background: #aaf;  /* blue */
}
<div id="outer">
    <div id="inner">
        BLAH BLAH BLAH BLAH BLAH BLAH
    </div>
</div>

Anyway, the solution is simple: don't do that. Either get rid of one of the nested divs entirely, or just set the width: attribute on the outer one, not the inner one.

Upvotes: 2

Related Questions