简体   繁体   中英

Why the wrapping on Firefox, with a display: block; overflow: hidden inside a display: inline-block?

Code

 #a { display: inline-block; } #b { float: left; } #c { display: block; overflow: hidden; } 
 <span id="a"> <span id="b">b</span> <span id="c">c1 c2</span> </span> 

On Chrome and IE, this gets rendered as:

铬

However, Firefox renders this as:

火狐

You can try it in this jsFiddle .

Questions

  1. Why this difference? Is this a Firefox bug? (If it is, a bug, I'd be interested in the link to the bug, so I can vote for it to be fixed.)
  2. How to get Firefox to render this like Chrome does, given some of the constraints mentioned in the next section?

Context

Here is just a little bit of context, without which what I'm trying to do above might seem crazy. I intend to use the above blocks for a label, like:

名字标签

The elements #a , #b , and #c correspond to:

元素对应的是什么

The CSS is designed to deal with the following constraints:

  1. In need this to work with IE8 (no flexbox for me).
  2. Block #a is clickable, and I don't want it to expand to the right more than is necessary, hence the inline-block . But obviously, another way to do the same would work.
  3. The content of block #c must wrap if it become too long, and shouldn't go "below" block #b , as shown below. This explain the overflow: hidden or display: table .

长标签

The Fix

Don't mix, embrace the table!

"Fixes" it on Firefox (tested 31.2.0) and IE 8, and is honestly more semantically correct.

 .a{ display:inline-table; max-width:45%; } .b{ display:table-cell; padding-right:5px;} .c{ display:table-cell; } 
 <span class="a"> <span class="b">b</span> <span class="c">c1 c2</span> </span> <br /><hr /><br /> <span class="a"> <span class="b">longer</span> <span class="c">Bacon ipsum dolor amet excepteur laboris irure, corned beef minim pastrami venison in anim incididunt strip steak ea non doner.</span> </span> 


Why There Was An Issue

The problem with your original code is that Firefox takes the floated element completely out of flow when calculating its shrink-to-fit sizing. While float s affect in-flow content width, they themselves are not in-flow, and thus, are merely taking the horizontal space from the rest of the content- causing the behavior you were seeing

Here is a demonstration of what is happening (view in Firefox to actually see it). Note that outline s are used on .a and .c but a thick border (which actually takes up space) is used on .b .

 .a { display: inline-block; outline: 1px solid blue; } .b { float: left; border: 5px solid green; } .c { display: table; outline: 1px solid red; } 
 <span class="a"> <span class="b">b</span> <span class="c">c1 c2</span> </span> <br /> <span class="c">c1 c2</span> 

This is by design, not a bug , and checking the current spec , the basic box model working draft , and the spec regarding calculating widths you'll find that floats are noted as out-of-flow, and the spec regarding block-formatting and inline-formatting explicitly states that float s are part of an inline-context width, but does not for a block-context (the inside of an inline-block ). Thus, Firefox is actually behaving according to a strict interpretation of the spec- this is technically a bug in the other browsers.

Yet, there is still an issue with how Firefox does this.

If we make the float larger than our in-flow content (and switch back to borders as outlines include overflowing children; I've shifted the sibling red left by 1px so it will line up with its nephew clone) (again, view in Firefox)

 .a { display: inline-block; border: 1px solid blue; } .b { float: left; border: 5px solid green; width:200px; } .c { display: table; border: 1px solid red; } .d { position:relative; left:1px; } .e { max-width:100px; } 
 <span class="a"> <span class="b">b</span> <span class="c">c1 c2</span> </span> <br /> <span class="cd">c1 c2</span> <br /> ^^^ without <em>.a</em> having a width limit <hr /> vvv with <em>.a</em> having a width limit <br /> <span class="ae"> <span class="b">b</span> <span class="c">c1 c2</span> </span> <br /> <span class="cd">c1 c2</span> 

The blue box is stretched to contain its entire green child, even though, according to the strict interpretation of the spec , it is supposed to overflow instead. THIS is a bug with Firefox, as floats are only supposed to affect parent width:auto in an inline context. In a block context, which the inside of an inline-block is supposed to be, the width of floats is not included.

Note, this is also how most other browsers behave (to a degree- Firefox will size the parent to the float 's box width, while other browsers will put the in-flow next to it if the parent can still grow), and that if you provide any width values or limits on .a (ala .e in my example), you get the expected behavior (as shown) of the green float overflowing its parent (should be the case in all browsers, as all of these behavioral issues are based on width:auto ).


Credits

Many beers to Oriol (poster of the other answer)- if I didn't start arguing with him on this, I wouldn't've had a reason to actually get into the specs and figure out what was really happening. Also credit to him for pointing out Firefox did still mis-render it- that entire chunk was thanks to him.

What happens under the hood

  1. According to inline-block width , the width of #a is calculated with the shrink-to-fit algorithm:

    If width is auto , the used value is the shrink-to-fit width

    The shrink-to-fit algorithm (now called fit-content measure ) is

    min( max-content , max( min-content , fill-available ))

    This means that, if there is enough space, its width will be the maximum preferred width of the contents (that is, without breaking lines other than where explicit line breaks occur). Otherwise, it will be as wide as the available space.

  2. Then, browsers calculate the preferred width of #a first.

    Usually, floats overlap following block boxes, as explained in Floats :

    Since a float is not in the flow, non-positioned block boxes created before and after the float box flow vertically as if the float did not exist.

    Then, Firefox seems to calculate that preferred width as if #b and #c could overlap. So the preferred width is the maximum of the width of the float #b and the width of #c .

    However, #c establishes a new block formatting context (BFC), due to overflow: hidden :

    block boxes with 'overflow' other than 'visible' [...] establish new block formatting contexts for their contents.

    In your fiddle, #c is a table instead, but it's the same because, as defined in Tables model ,

    The table wrapper box establishes a block formatting context.

    But as explained in BFC next to floats , BFC roots can't overlap floats:

    The border box of a table, a block-level replaced element, or an element in the normal flow that establishes a new block formatting context (such as an element with 'overflow' other than 'visible') must not overlap the margin box of any floats in the same block formatting context as the element itself.

    Instead, the float can "push" the BFC to the right. That's why on Chrome and Edge, the preferred width of #a is the sum of the preferred widths of #b and #c .

  3. The width of #a will then be the preferred width calculated in the previous step in case it's not greater than the available width.

    Otherwise, browsers will calculate the preferred minimum width by introducing a line break between #b and #c . So it will be the maximum of the minimum preferred widths of #b and #c . And the final width of #a will be the maximum between that preferred minimum width and the available width.

  4. Now the width of #a has been resolved, so the widths of #b and #c can be resolved too.

    Since #b is floated, it will be sized with the shrink-to-fit algorithm too.

    If #c is a block, it will take the remaining space (if there isn't available space, it will be moved to the next line). If it's a table, the default automatic table layout algorithm is implementation-dependent, but similar to shrink-to-fit. The result is basically the same.

    In case of Chrome and Edge, if there was enough space, the remaining space taken by #c is its preferred width, because the preferred width of #a was the sum of #b and #c .

    However, Firefox used the maximum. Therefore, if #b is wider than #c , #b will fill #a completely, and #c will be moved to the next line. If #c is wider than #b , there will be some remaining space next to #b , so #c will shrink to fit there. That shrinkment causes #c to be smaller than its preferred width (even if #a has available space to grow more), so some line break will appear in #c .

Summary

  1. By default, a block grows to fill the available width of its containing block.
  2. If there is a float smaller than the containing block, the following block will overlap it and still be as wide as the containing block.
  3. But if that following block establishes a BFC, it will shrink to avoid overlapping the float, and will only fill the remaining space left by the float.
  4. By default, a float or an inline-block attempts to be as wide as its contents want.

Chrome and Edge first detect the BFC root won't overlap the float, and then the width of the inline-block parent becomes the sum of the float and the BFC root.

Instead, Firefox initially sees the BFC root as a normal block, it thinks it will overlap the float, and calculates the width of the parent accordingly. Then detects it's a BFC root, prevents the overlap, and shrinks it to fit in the remaining space.

So the difference seems that they calculate the width of the parent and prevent BFC roots from overlapping floats in different order.

The incorrect behavior seems Firefox's. As defined in Intrinsic Sizes of Non-Replaced Blocks , the preferred width should be calculated after layout, and most probably layout includes pushing BFC roots next to floats.

The max-content measure of a block container box is the measure of the box after layout, if all children are sized under a max-size constraint .

Fix

Using an inline-level display like display: inline-block or display: inline-table on #c makes Firefox sum the widths of #b and #c when calculating the width of #a . This way, if there is enough available space, #c won't shrink and no line breaks will appear.

The problem is that, in case the contents of the inline-level are too wide to fit in the remaining space left by the float, the inline-level will be moved to the next line instead of shrinking.

 .a { display: inline-block; } .b { float: left; } .c { display: inline-table; } 
 <span class="a"> <span class="b">b</span> <span class="c">c1 c2</span> </span> <hr /> <span class="a"> <span class="b">longer</span> <span class="c">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut quis diam nec ligula iaculis lacinia non ac neque. Vivamus ut condimentum enim, et sollicitudin magna. In rhoncus nisi at est rhoncus feugiat sed vitae lacus. Maecenas sed felis et libero posuere iaculis a in lacus. Quisque eleifend auctor metus, a congue sapien venenatis a. Duis mollis mauris vitae massa mollis, nec porta nulla semper. Proin fringilla et nibh ac tempor. Aenean et augue ut dui pharetra scelerisque sed sit amet dolor. Nulla posuere a lorem sit amet vehicula. Morbi nec lacinia nibh. Suspendisse lacus nulla, dignissim et mi ut, luctus lobortis nisl. </span> </span> 

Alternatively, if old browser support is not a problem, you can use flexbox. A simple #a { display: inline-flex; } #a { display: inline-flex; } would basically work, but the following is closer to your current code.

 .a { display: inline-flex; align-items: flex-start; flex-wrap: wrap; /* Allow line breaks if #b is too wide */ } .c { flex: 1; /* Initial width of 0, then take remaining space left by #b */ } 
 <span class="a"> <span class="b">b</span> <span class="c">c1 c2</span> </span> <hr /> <span class="a"> <span class="b">longer</span> <span class="c">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut quis diam nec ligula iaculis lacinia non ac neque. Vivamus ut condimentum enim, et sollicitudin magna. In rhoncus nisi at est rhoncus feugiat sed vitae lacus. Maecenas sed felis et libero posuere iaculis a in lacus. Quisque eleifend auctor metus, a congue sapien venenatis a. Duis mollis mauris vitae massa mollis, nec porta nulla semper. Proin fringilla et nibh ac tempor. Aenean et augue ut dui pharetra scelerisque sed sit amet dolor. Nulla posuere a lorem sit amet vehicula. Morbi nec lacinia nibh. Suspendisse lacus nulla, dignissim et mi ut, luctus lobortis nisl. </span> </span> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM