20

This is a common problem but I can't figure out why it happens.

I have a parent div and inside that div I have 3 divs with width set to 33% (exactly, not 33.3%!) and display: inline-block.

In Chrome it works well, but in Mozilla and Opera it does not (I didn't test it in IE yet). I thought the problem might be in the algorithm browsers use to calculate pixel sizing from percentages. But when I checked the DOM metrics, I found that the parent's width is 864px and the child's is 285px (that's correct: 864 * .33 = 285.12). But why doesn't it fit in the parent? 285 * 3 = 855, that's 9px less than parent's width!

Oh, yes, padding, margin and border for all divs set to 0 and DOM metrics confirm that.

Matt Coughlin
  • 18,666
  • 3
  • 46
  • 59
Roman Bekkiev
  • 3,010
  • 1
  • 24
  • 31

7 Answers7

41

Whitespace in the HTML source code

In the HTML source code, When you have lines of text or images, or elements that are inline-block, if there is any whitespace between them (blank spaces, tabs, or new lines), a single blank space character will be added between them when the page is rendered. For example, in the following HTML, a blank space will appear between each of the four pieces of content:

one
two
<img src="three.png"/>
<span style="display: inline-block;">four<span>

This is very helpful for lines of text, and for small images or HTML elements that appear inside a line of text. But it becomes a problem when inline-block is used for layout purposes, rather than as way to add content inside a paragraph of text.

Removing the extra space

The safest, cross-browser way to avoid the extra 4px or so of space that's added between inline-block elements, is to remove any whitespace in the HTML source code between the HTML tags.

For instance, if you have a ul with 3 floated li tags:

<-- No space, tabs, or line breaks between </li> and <li> -->
<ul>
    <li>...</li><li>...</li><li>...</li>   
</ul>

Unfortunately, this hurts the maintainability of the website. Besides making the code unreadable, it severely compromises the separation of data and formatting.

If another programmer comes along later and decides to put each li tag on a separate line in the source code (unaware of why the tags were on the same line, or possibly running it through HTML Tidy and not even having a chance to notice any related HTML comments), suddenly the website has a formatting bug that may be difficult to identify.

Consider floating elements instead

The whitespace behavior strongly suggests that it may be inappropriate to use inline-block for general-layout purposes, to use it for anything other than adding content inside the flow of a paragraph of text.

Plus, in some cases inline-block content is very difficult to fully style and align, especially on older browsers.

Quick summary of other solutions

  1. Put the close tag on the same line as the next open tag, with no white space between them.
  2. Use HTML comments to fill all of the whitespace between the close tag and the next open tag (as @Arbel suggested).
  3. Add a negative left margin to each element (usually -3px or -4px, based on the font-size). I don't recommend this particular approach.
  4. Set the font-size for the container element to 0 or 0.01em. This doesn't work in Safari 5 (not sure about later versions), and it may interfere with Responsive Design websites, or any website that uses a font-size unit other than px.
  5. Remove whitespace-only text nodes from the container using JavaScript or jQuery. This doesn't work in IE8 and earlier, as text nodes are not created in those browsers when there's only whitespace between elements, though space is still added between the elements.
  6. Set letter-spacing and word-spacing for the container (as @PhillipWills suggested). Further info. This requires standardizing em sizes on the website, which may not be a reasonable option for all websites.
  7. Add text-space-collapse: discard; to the container (previously called white-space-collapse). Unfortunately, this CSS3 style is not yet supported by any browsers, and the standard hasn't been fully defined.
Community
  • 1
  • 1
Matt Coughlin
  • 18,666
  • 3
  • 46
  • 59
  • 6
    This is a great overview but you’re missing the best solution to this issue which is to use the `display:flex` layout paradigm instead. Here is a current list of browsers that support it: http://caniuse.com/#feat=flexbox – Zaqx Nov 27 '14 at 20:05
6

If you don't want to mess up the HTML formatting e.g. having all the elements with inline-block written in one line for future readability and also get rid of the extra white space that is added between them, you can "comment" the white space.

For example in your code this will solve the problem, it will even work with 33.3% instead of 33%:

.parent {
    width: 100%;
 }
.child{
    display: inline-block;
    width: 33.3%;
}

/\

<div class="parent">
       <div class="child">bla-bla1</div><!-- 
    --><div class="child">bla-bla2</div><!-- 
    --><div class="child">bla-bla3</div>
</div>
Abhinav Aggarwal
  • 1,205
  • 2
  • 11
  • 24
Arbel
  • 30,599
  • 2
  • 28
  • 29
  • 1
    This should work fine, though it does present its own maintainability issues (Will the existence of the comments confuse other programmers? Will the need for the comments be obvious?). Adding descriptive text to the comments might help a little, but on the whole this is another argument in favor of floating elements. – Matt Coughlin Mar 27 '13 at 15:29
4

A space is added between the inner divs. There is some CSS voodoo to correct this problem:

div {
    letter-spacing: -.31em;
    word-spacing: -.43em;
}
div div {
    letter-spacing: normal;
    word-spacing: normal;
}

Of course, you'll probably prefer to use classes or something to differentiate between parent and children.

philwills
  • 985
  • 4
  • 8
  • Note: This solution requires standardizing `em` sizes on the website, which may not be reasonable for all websites. [Further info](http://stackoverflow.com/a/5078358/1306809). – Matt Coughlin Mar 27 '13 at 15:04
4

Add float:left;

.parent{
    width: 100%
}
.child{
    float:left;
    display: inline-block;
    width: 33%

}

http://jsfiddle.net/H6Whc/1/

Avin Varghese
  • 4,340
  • 1
  • 22
  • 31
  • 4
    If the element is floated, `inline-block` is no longer needed. Floated elements are implicitly block-level (by default growing to the size of their content), regardless of the value set for the `display` property. – Matt Coughlin Mar 27 '13 at 15:08
2

Has anyone tried display: table? If that's not a good idea, why not? This works in all modern browsers and I tested it down to IE9.

.parent{  
    display: table;
    width: 100%;
}
.containers {
    box-sizing: border-box;
    border: 1px solid #000;
    height: 50px;
    width: 33.3%;
    display: table-cell;
}
eaglejs
  • 173
  • 1
  • 10
1

This is a mentioned by a number of comments and by @Avin, but removing display: inline-block and replacing it with float: left works.

.parent{
    width: 100%
}
.child{
    float:left;
    width: 33%
}
bvpb
  • 1,416
  • 22
  • 31
-1

This is a common problem, but it can be sorted out very easily by assigning the display: table CSS property to the parent div.

The Bearded Llama
  • 3,036
  • 4
  • 20
  • 31
Vivek Mehta
  • 734
  • 8
  • 10