22

After reading two great answers explaining the behaviour of inline-block elements (Why is this inline-block element pushed downward? and why the span's line-height is useless) I still have two unexplained questions.

1. What the reason to change baseline of inline-block element from baseline of its line box to bottom margin edge?

http://www.w3.org/TR/CSS2/visudet.html#leading

The baseline of an 'inline-block' is the baseline of its last line box in the normal flow, unless it has either no in-flow line boxes or if its 'overflow' property has a computed value other than 'visible', in which case the baseline is the bottom margin edge.

2. How to calculate this shift?

enter image description here

Important: I don't try to find a solution how to fix it. I try to understand what was the reason to change positioning behaviour of inline-block element when it is applied overflow: hidden. So please, don't post answers for dummies.

UPDATE

Unfortunately I didn't get what I want although I accepted the answer. I think the problem in the questions itself. Regarding the first question: I wanted to understand why inline-block can't preserve baseline of its line box even if it has overflow:hidden (despite of W3C specification of course). I wanted to hear the design decisions - not just it must be set to something, because W3C it mandates. The second one: I want to get a formula where we can paste font-size and line-height of an element and get the correct result.

Anyway thanks to anybody :)

UPDATE 2

Fortunately and subjectively the answer is found! See the first re-accepted answer. Thank you, @pallxk!)

Community
  • 1
  • 1
Timur Fayzrakhmanov
  • 17,967
  • 20
  • 64
  • 95
  • Is there a reason you need to calculate the shift? You can make them line up using `vertical-align` on the `inline-block` elements if that's all you're after – BCDeWitt Aug 18 '15 at 18:11
  • I know that `vertical-align:top` fix this shift, but it's just interesting where the gap is come from. – Timur Fayzrakhmanov Aug 18 '15 at 18:19
  • 1
    I agree and find it even more interesting that any `vertical-align` setting seems to fix it in my tests so far. – BCDeWitt Aug 18 '15 at 19:14
  • 1
    On 1, as it stands, the scrolling of the inline-block element only affects the rendering of the content of that element. If the baseline moved when the user scrolled the element, other aligned elements would move, and consequently the whole page layout could be affected. – Alohci Aug 18 '15 at 23:54
  • Looks interesting, but unfortunately, I can't grasp your idea and understand what exactly you mean. Please, could you give some example(s)? – Timur Fayzrakhmanov Aug 19 '15 at 08:30
  • overflow doesn't fix it completely. – aimme Aug 23 '15 at 23:30
  • @aimme)) I don't try to find a solution how to "fix" it. I try to understand what was the reason to change positioning behaviour of `inline-block` element when it is applied `overflow: hidden`. – Timur Fayzrakhmanov Aug 24 '15 at 14:26
  • 1
    overflow + size set will not show or mind overflowing content. margins on inline-block element increase size/room needed and will increase the line-height as well. test : http://codepen.io/gc-nomade/pen/BoBeKo – G-Cyrillus Aug 29 '15 at 19:21
  • @TimurFayzrakhmanov just now posted my another answer briefing everything you need to know about this. hope it helps :) i am 100% sure thats what you are seeking.. enjoy :) – aimme Aug 30 '15 at 09:37

5 Answers5

15

1. What the reason to change baseline of inline-block element from baseline of its line box to bottom margin edge?

The baseline of an 'inline-block' is changed to its bottom margin edge when its overflow property is set to hidden (full specification here).

As for the reason for this decision, I think since the overflown part is hidden, user agents (browsers) may choose to render that overflown part and not display it, or choose to not render it at all. And when the overflown part is not rendered, user agents have no way to tell the baseline of its last line box, as it is not rendered, where it goes is not known.

If the baseline of 'inline-block' whose overflow is set to hidden is still kept as the baseline of its last line box, user agents are forced to render what is hidden to user, which may hinder performance, or at least, put extra restrictions on user agents. What's more, in such case, other inline texts in the same line box are aligned to such a baseline where texts around the overflow-hidden inline-box is hidden, which is kind of stange and not intuitive.

I made a simple demo emulating that inline-block with overflow hidden still has its baseline set to the baseline of its last line box.

emultaing_imaginary_baseline_of_overflow_hidden_inline_block

var isOverflowHidden = false;
document.querySelector('button').onclick = function() {
  document.getElementById('inline-box').style.overflow = isOverflowHidden ? '' : 'hidden';
  isOverflowHidden = !isOverflowHidden;
}
html { background: white; }
#inline-box { display: inline-block; height: 18px; }
.overflown { color: white; }
<p><button id="toggle">Toggle 'overflow: hidden;' on 'inline-block'</button></p>

<span>
  texts sit
  <span id="inline-box">
    texts in inline-block <br>
    <span class="overflown">
      line 2 <br>
      line 3
    </span>
  </span>
  on baseline
</span>

Besides, you may also compare this behavior with display: none. When that's set, clientWidth and clientHeight both equates to 0.

2. How to calculate this shift?

This part is much easier, since it's documented in the link you gave in the question.

I'll start from the definition of 'line-height'.

The height of the inline box encloses all glyphs and their half-leading on each side and is thus exactly 'line-height'.

That is, line-height is composed of, from top to bottom, top half-leading + height(ascent) + depth(descent) + bottom half-leading.

Height of each component can be calculated for a given font at a given size.

Basically, every font has font metrics that specify a characteristic height above the baseline and a depth below it.

Take 'Times New Roman' as an example, using FontForge, we see that it has Em Size as 2048, HHead Ascent as 1825, and HHead Descent as -443. That is, 1825 / 2048 = 89.1% of font-size contributes to the ascent, and 443 / 2048 = 21.6% contributes to the descent.

FontForge

There are also metrics starting with 'Typo', that category will be used if 'Really use Typo metrics' is checked, and the spec recommends this:

Note. It is recommended that implementations that use OpenType or TrueType fonts use the metrics "sTypoAscender" and "sTypoDescender" from the font's OS/2 table for A and D (after scaling to the current element's font size). In the absence of these metrics, the "Ascent" and "Descent" metrics from the HHEA table should be used.

Line-height minus ascent and descent is the so-called leading.

Half the leading is added above A(ascent) and the other half below D(descent).

Assuming a font-family: Times New Roman; font-size: 100px; line-height: 200px;, we get

ascent = 100px * (1825 / 2048) = 89px
descent = 100px * (443 / 2048) = 22px
top half-leading = bottom half-leading = (200px - 89px - 22px) / 2 = 44.5px

So we see that this can be calculated. And this can also be measured on pages.

Here is another demo for you to fiddle with.

If you are asking for the shift of bottom half-leading, it's shown as space between green line and blue line in the code snippet. If you are asking for the shift of descent and bottom half-leading, it's shown as space between red line and blue line in the code snippet.

var $ = document.querySelector.bind(document);

var fontFamily = window.getComputedStyle($('#examinee'))['font-family']
  , fontSize = +window.getComputedStyle($('#examinee'))['font-size'].replace('px', '')
  , containerLineHeight = +window.getComputedStyle($('#examinee'))['line-height'].replace('px', '')
  , textLineHeight = $('.target').offsetHeight
  , ascent = $('#examinee .baseline').offsetTop + $('#examinee .baseline').offsetHeight - $('#examinee .text-top').offsetTop
  , descent = $('#examinee .text-bottom').offsetTop - $('#examinee .baseline').offsetTop
  , topHalfLeading = $('#examinee .text-top').offsetTop
  , bottomHalfLeading = $('#examinee').offsetHeight - 2/* borders of the container */ - $('#examinee .text-bottom').offsetTop - $('#examinee .text-bottom').offsetHeight;

$('#font-family').innerText = fontFamily;
$('#font-size').innerText = fontSize + 'px';
$('#container-line-height').innerText = containerLineHeight + 'px';
$('#text-line-height').innerText = textLineHeight + 'px';
$('#ascent').innerText = ascent + 'px';
$('#descent').innerText = descent + 'px';
$('#top-half-leading').innerText = topHalfLeading + 'px';
$('#bottom-half-leading').innerText = bottomHalfLeading + 'px';
div {
  font-size: 20px;
  line-height: 2;
  width: 650px;
  
  border: 1px dashed gray;
  border-top: 1px solid blue;
  border-bottom: 1px solid blue;
  margin: 1rem 0;
  overflow: hidden;
  white-space: nowrap;
}

span:not([class]) {
  display: inline-block;
  border: 1px dashed gray;
}

.baseline,
.text-bottom,
.text-top {
  display: inline-block;
  width: 200%;
  margin: 0 -100%;
}

.baseline {
  border-bottom: 1px solid red;
  vertical-align: baseline;  /* the default */
}

.text-bottom {
  border-bottom: 1px solid green;
  vertical-align: text-bottom;
}

.text-top {
  border-bottom: 1px solid green;
  vertical-align: text-top;
}

#examinee {
  position: relative;
  font-size: 100px;
  line-height: 200px;
}
<p>
  Demonstrates that "overflow: hidden;" sets baseline of an inline-block element to its bottom margin.
</p>
<div>
  <span class="baseline"></span>
  <span class="text-top"></span>
  <span class="text-bottom"></span>
  &lt;div&gt;
  <span>
    &lt;span style=""&gt;&lt;/span&gt;
  </span>
  &lt;/div&gt;
</div>
<div>
  <span class="baseline"></span>
  <span class="text-top"></span>
  <span class="text-bottom"></span>
  &lt;div&gt;
  <span style="overflow: hidden;">
    &lt;span style="overflow: hidden;"&gt;&lt;/span&gt;
  </span>
  &lt;/div&gt;
</div>

<p>
  Demonstrates the position of baseline, text-top and text-bottom. <br>
  Demonstrates how "line-height" affects box sizing.
</p>

<ul>
  <li>Blue lines: top and bottom borders of line boxes
  <li>Red lines: baseline of texts
  <li>Green lines: text-top or text-bottom of texts
</ul>

<ul>
  <li>Between blue lines: the line-height
  <li>Between red line and green line: ascent or descent
</ul>

<div id="examinee">
  <span class="target">GgJjPpQqYy</span>
  <span class="baseline"></span>
  <span class="text-top"></span>
  <span class="text-bottom"></span>
</div>


Measured metrics:
<ul>
  <li>font-family: <span id="font-family"></span></li>
  <li>font-size: <span id="font-size"></span></li>
  <li>container line-height: <span id="container-line-height"></span></li>
  <li>text line-height: <span id="text-line-height"></span></li>
  <li>ascent: <span id="ascent"></span></li>
  <li>descent: <span id="descent"></span></li>
  <li>top half-leading: <span id="top-half-leading"></span></li>
  <li>bottom half-leading: <span id="bottom-half-leading"></span></li>
</ul>
line-height measuring
pallxk
  • 1,057
  • 14
  • 14
2

This might not be the answer. but it might help to resolve this issue by removing the extra space in between inline-block elements.

<style>
.main_div {
    display:table;
    border-collapse:collapse;
    width:100%;
    border:1px solid red;
}
.main_div span {
    display:table-cell;
    border:1px solid black;
    height:20px;
    border:1px solid green;
}

</style>
<div class="main_div">
    <span class="one">one</span>
    <span class="two">two</span>
    <span class="three">three</span>
</div>
aimme
  • 6,385
  • 7
  • 48
  • 65
2

@timur: First of all nice question.

I do not know whether it would answer your question but I would like to talk about some behaviour aspect of "inline-block" elements.

First of all, "inline-block" elements act according to its sibling elements and its content.

If there are two divs, one beside the other and both have display:inline-block; property:value then it would depend on the contents inside each div and would start displaying the content from the baseline which is the natural behaviour of "inline-block element".

Now, let me explain you "overflow" property behaviour.

By default, overflow property is "Visible" and its depended property is "overflow-wrap: normal;". Also it gets applied to only "block" level and "inline-block" elements because inline elements are those elements which wrap to the text and there are no white space inside the content to stop overflowing.

So the span in the example you have provided has to be "block" OR "inline-block" to apply overflow and vertical-align.

IF you look at this fiddle ---> http://jsfiddle.net/Lkyd1kr0/1/ where I have used "inline-block" for the second span element.

HTML

<div class="one"><span>as</span></div><div class="two"><span>asd</span></div>

CSS

.one,.two { 
width: 200px;
display: inline-block ;

}

.one { border: 2px solid #f00; }
.two { border: 2px solid #000; }

.one span { display: block; }

.two span { 
    display: inline-block;
    overflow:hidden;
}

Now, just use Web developer tools and hover over the div.two and div.two > span and check the difference in height.

This is due to, as span is the content of the div.two who is inline-block and the rest of the space on the height of the div.two is secured for the rest of the content which is white space. This behavior you would generally see on "block" level and "inline-block" level elements.

Also, notice "transform-origin" of both "span" and "div.two" it would have 4px of difference on y-axis.

  • Thanks for the explanation!)) Unfortunately, there is nothing new to me. The most interesting sentence is: `the rest of the space on the height of the div.two is secured for the rest of the content which is white space`. What does it mean "secured"? To secure what? What is "white space" in this context? Another moment confused me is `the span in the example has to be "block" OR "inline-block" to apply overflow and vertical-align`. Vertical-align can be applied to b, i-b, i etc. What did you mean? – Timur Fayzrakhmanov Aug 26 '15 at 14:55
  • 1
    @TimurFayzrakhmanov: I meant block level and inline-block level doesn't wrap up the content and hence always left with the extra space at the content area for more text. Span is an inline element. And overflow property does not get applied to inline element. reference: [link]https://developer.mozilla.org/en-US/docs/Web/CSS/overflow Hence, we need to apply block level OR inline-block level property to span element. – Imran Sheikh Aug 26 '15 at 15:56
  • I know that overflow is visible by default, I know that span is inline by default, I know that block and inline-block level elements doesn't wrap up the content by default. It's common things - there is nothing to speak) However question is still open: what the reason to move baseline from its line box to bottom margin edge for inline-block elements with overflow other than visible? Why it can't remain the position. – Timur Fayzrakhmanov Aug 26 '15 at 17:25
2

1. What the reason to change baseline of inline-block element from baseline of its line box to bottom margin edge?

Its the default line-height or the normal line-height of its parent inherited to the inline-block element or the default value defined by the user agent which is by the way W3C recommended line-height:normal.

Reference here

http://www.w3.org/TR/CSS2/visudet.html#propdef-line-height says

normal

Tells user agents to set the used value to a "reasonable" value based on the font of the element. The value has the same meaning as .

We recommend a used value for 'normal' between 1.0 to 1.2. The computed value is 'normal'.

So we know the default is line-height:normal and it inherits. Now we know that inline-block adds the element the line-height and lets see what happens when overflow:hidden is set

W3C - Overflow says

  • hidden

The content is clipped and no scrollbars are provided.

overflow:hidden and display:inline-block creates space for the hanging characters like pgy. as it is block and inline, overflow preserves both space for the hanging characters that might come and line-height.

enter image description here

2. How to calculate this shift?

its browser specific and so we would need to refer browser specification

See here to know the value of 'normal'

mozilla

Initial value normal
Inherited yes

normal Depends on the user agent. Desktop browsers (including Firefox) use a default value of roughly 1.2, depending on the element's font-family.

To understand the image, See the HTML CSS below

enter image description here

The Second div and Second Div Span has line-height:normal (which is 1.2 normally) because normal is inherited as its not defined.

The Third div and Third Div Span has line-height:40px which is defined, The value of the third div line-height was by default inherited to its inline child elements too.

CSS/Properties/display says

  • inline-block Causes an element to generate an inline-level block container. The inside of an inline-block is formatted as a block box, and the element itself is formatted as an atomic inline-level box.

As the term inline says it means the element is an inline element, and the block means its block. so together it means its a block element defined in the same line here is the example proof that shows three situations By default DIV is a block-Level element, and SPAN is an Inline Element. More information here HTML Block and Inline Elements

<style>
.first-div{
    border:#F00 1px solid;
}
.first-div span{
    /**by default span is an inline element.**/
    border:#093 1px solid;
}
.second-div{
    /** line-height not defined default line height will be used **/
    border:#F00 1px solid;
}
.second-div span{
    /** default line height inherited from the secod-div **/
    display:inline-block;
    border:#093 1px solid;
    overflow:hidden;
}
.third-div{
    border:#F00 1px solid;
    /**see line height. we change the default line height**/
    line-height:40px;
}
.third-div span{
    /**see line height. line height 40px set in the parent will be used**/
    display:inline-block;
    border:#093 1px solid;
    overflow:hidden;
}
</style>
<div class="first-div">
First Div
<span>First Span</span>
</div>
<br/>
<div class="second-div">
Second Div
<span>Second Span</span>
</div>
<br/>
<div class="third-div">
Third Div
<span>Third Span</span>
</div>
aimme
  • 6,385
  • 7
  • 48
  • 65
  • 2
    Hello, thanks for big explanation. I accept your answer, because you make an effort. Unfortunately I didn't get what I want. I think the problem in the questions itself. Regarding the first question: I wanted to understand why `inline-block` can't preserve baseline of its line box even if it has `overflow:hidden` (despite of W3C specification of course); I wanted to hear design decisions, not just it *must* be set to something, because W3C it mandate. The second one: I want to get a formula where we can paste `font-size` and `line-height` of parent element and get the correct result. – Timur Fayzrakhmanov Aug 31 '15 at 07:38
1

It's not will looks like answer, but I found a very interesting thing on this example.

If we take a lool to #firstDiv we can see some - margin in the bottom. But I think this margin taken from the height of horizontal scrollbar.

Scrollbar

I take a height of it, the height is near 14px;

Then take a height of top margin of out inline-block with overflow: hidden it's a 15px, and without overflow: hidden it's near 30px.

15px margin 30px margin

Is it a coincidence? I do not think. The same trick will be with larger height of #container.

P.S. This response does not purport to answer, I just could not put it all in a comment. I do not know how it's all connected, I just noticed this. Thank you for understanding.

AleshaOleg
  • 2,171
  • 1
  • 15
  • 29
  • The shift has nothing to do with horizontal scrollbar)) The value is based on `line-height` and `font-size` of a parent node. I found it has always 15% if `line-height` and `font-size` values are the same. But if they are different from each other it leads to more difficult calculations which I can't determine). Привет Украине!) Как там дела? Спокойно или все еще создают шум? – Timur Fayzrakhmanov Aug 24 '15 at 11:27
  • @TimurFayzrakhmanov "I found it has always 15% if line-height and font-size values are the same" - it's really very strange. But I think both of our guess are wrong:( Спасибо. Я с Киева, тут ничего такого нет. Не могу ничего сказать. – AleshaOleg Aug 24 '15 at 22:25