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?
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!)
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.
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.
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>
<div>
<span>
<span style=""></span>
</span>
</div>
</div>
<div>
<span class="baseline"></span>
<span class="text-top"></span>
<span class="text-bottom"></span>
<div>
<span style="overflow: hidden;">
<span style="overflow: hidden;"></span>
</span>
</div>
</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>

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>
@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.
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.
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.
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.
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.
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'
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
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.
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>
来源:https://stackoverflow.com/questions/32078950/why-baseline-of-inline-block-element-with-overflowhidden-is-set-to-its-bott