Erik Funkenbusch
Erik Funkenbusch

Reputation: 93444

Block element inside inline-block acting strangely in Firefox

Can anyone explain this behavior in FF?

Fiddle: http://jsfiddle.net/4mrt8wq3/

<style>
    .b { display: inline-block; }
    #a { display: block; }
</style>

<div class="b">
    <label>xxxxxxxxxx</label> 
    <input type="text" id="a"/>
</div>
<div class="b">
    <label>xxxxxxxxxx</label>
    <div> / </div>
</div>

Only in firefox, the first div is positioned one line lower than the second. It works correctly in Chrome and IE (at least IE11). It's as if the block element within the inline-block is wrapping below the second element for some reason.

Using overflow: hidden on the first div fixes the problem, but the second div is then positioned slightly oddly with about 4 or 5 pixels of margin above it. Placing overflow-hidden on both causes it to render correctly.

I am not looking for a solution to the problem, as I've already found one, but I'm at a loss of explaining the behavior... Can anyone explain WHY it's doing this?

Upvotes: 5

Views: 2693

Answers (2)

Alohci
Alohci

Reputation: 82986

Yes, interesting question. First we need understand that the default vertical alignment of inline-block elements is baseline, and the baseline of each such element is the baseline of the last line box in them.

In the second div with class "b", the inner div itself contains a line box to hold the '/' character. That then provides the baseline for the second div with class "b".

That baseline must align level with the baseline of the first div with class "b". The question becomes: where is the baseline of the last line box in that div?

By making the input element itself display:block, Firefox¹ takes the view that the input element is "replaced", it's contents are opaque to CSS, therefore no line box is ever created by the input element. So the last line of the first div with class "b" is the one containing the label, and that is aligned level with the line of the '/' character.

Chrome takes a different view. Chrome treats the input element as having an internal structure visible to CSS, so the innards of the element form a line box, whose baseline then becomes the baseline of the first div with class "b", and it is that which aligned level with the '/' character.

When you add `overflow:hidden', it affects the baseline of the inline-blocks such that their baselines cease to be the baseline of their last contained line box, and becomes the bottom margin edge of the element.


Which behaviour is correct is unclear. It depends on history and the somewhat adulterated notion of replaced elements. In the early days of browsers, the rendering of some elements was delegated to external systems, either the underlying operating system or a plug-in. In particular, this was true of the input element, where rendering was done by O/S calls. The O/S had no notion of CSS, so rules had to be defined to allow the effectively black boxes to interact with the rest of the page. Such elements were classified as "replaced" elements.

Note the way this is defined. There is no official list of elements that are replaced elements, an element is a replaced element if the browser chooses to delegate its rendering to a system outside the CSS world, so in theory you could have two browsers, one delegating the rendering of an element and one natively rendering it, and from the CSS rules get quite different interactions.

As browsers progressed, they stopped delegating their rendering of the input element and rendered it themselves, in the process making the renderings CSS aware. This causes a problem because extant web pages, which assume that the input elements will be rendered using the replaced elements' rules, can become unusable. If a browser allowed that to happen, it would lose market share. So for the most part, to avoid this, the browsers implement those elements' layouts to interact with the page as if they were replaced elements, even though in reality they are not.

How far they go in this respect is not well specified. The HTML5 spec does not recognise the form controls as replaced elements, and suggests that they be rendered as inline-block, which would make Chrome's behaviour correct, but there are many ways in which all the browsers including Chrome simply don't behave that way. From a backward compatibility perspective with old web content, the Firefox behaviour is more reliable.

Until the layout of form controls is specified much more tightly than is the case currently, it is impossible to conclusively say which behaviour is correct.


¹For me, IE11 behaves like Firefox. Opera 28 (blink engine like Chrome) behaves like Chrome. Opera 12 (presto engine) behaves like Firefox.

Upvotes: 8

Guarana
Guarana

Reputation: 195

Your problem is that per spec setting overflow:hidden changes the baseline position of an inline-block. Firefox implements what the spec says. Chrome does not.

Solution:

<style>
  .b { display: inline-block; 
  vertical-align: top; /*add this line */
  }
  #a { display: block; }
</style>

Upvotes: 0

Related Questions