I'm trying to use floating divs inside the element with the display:table-cell property, but I have encountered unexpected behavior.
Why are the elements in the first column affected by the elements in the second column (a in the first column is suddenly moved down)? Is there any way to prevent that behavior?
.table {
display: table;
}
.cell {
display: table-cell;
}
.cell a {
padding: .5em;
border: .1em solid black;
}
.cell+.cell a {
float: left;
}
<div class='table'>
<div class='cell'>
<a>cell1</a>
</div>
<div class='cell'>
<a>cell2</a>
<a>cell2</a>
</div>
</div>
Fascinating behavior. I have several theories. Although I agree that using display: table-cell seems random, there are occasional uses for it so I'll leave that in there for my answer. I'm sorry if my answer is overtly complex; I'll try to simplify it:
First of all
To understand why the element is pushed down is a matter of understanding how vertical-alignment works for table-cells:
The only vertical-align property values that should be used for table-cells are top, middle, and bottom (see this excellent css-tricks article). Anything else is undefined behavior--meaning browsers may do whatever they want with them, and different browsers will probably do different things. This is probably not [ever...] what we want.
By default, browsers will give all td elements vertical-align: middle. So when you're creating a normal HTML table, you never necessarily have to worry about the vertical-align property.
On the other hand, all normal elements, by default, have vertical-align: baseline. This value on elements with display: table-cell causes our dreaded undefined behavior.
You are manually constructing a table using elements that won't, by default, behave like a table (this is why you probably shouldn't be using display: table-cell when you're not completely sure what you're doing). You therefore have to put all the default styles of a table on by yourself. This includes specifying a vertical-align: top|middle|bottom property for your .cells.
So why is it only pushed down when I float the elements in one cell?
Again, it's undefined behavior. When you don't float, this doesn't seem to cause a problem; the elements are still able to intelligently find the baseline of the inner text and vertically align themselves so they appear where you'd expect them. When you float here, there are two things to note:
1) Remember that floating removes an element from the normal flow of the DOM. This means other elements, including the element's parent, can't interact with that element like they normally would.
2) Remember that table-cells are intelligent; they can dynamically, vertically position their children.
Combining these two leads me to think that when you float those elements:
1) Their .cell parent is losing track of where the text is inside them.
2) It can't properly communicate to its neighbor cell where the baseline is--it just uses its bottom edge.
3) The neighbor cell correctly vertically aligns its content to that baseline, resulting in the shift downward.
The fix?
Put vertical-align: middle responsibly on your .cells:
.cell {
display: table-cell;
vertical-align: middle;
}
(And the a tags should probably be display: inline-block too). Here's an updated JSFiddle. Regards.
You have a plus sign (i.e.: adjacent siblings selector) in that rule .cell+.cell a, removing it works as expected:
.cell a {
float: left;
}
http://jsfiddle.net/vg5j7xgc/5/
CSS 2.1 says
The baseline of a cell is the baseline of the first in-flow line box
in the cell, or the first in-flow table-row in the cell, whichever
comes first. If there is no such line box or table-row, the baseline
is the bottom of content edge of the cell box.
This is the key to understanding the behaviour. It's very technical, so I'll try to pick it apart.
The first cell has an in-flow line box, formed by the text "cell1". Its baseline is the text's baseline.
For the second cell, all the non-whitespace text is removed from the flow by `float:left' and all the whitespace is removed by the normal whitespace rules. Therefore, it doesn't have any in-flow line boxes. Nor does it contain any in-flow table rows, so the baseline of cell is bottom of the content edge of the cell box.
Since the table-cell forms a block formatting context, the cell's content includes the floats, so the bottom content edge is the bottom of the floats.
The first cell has a vertical-align value of baseline by default. Therefore, it must be aligned with the baseline of the second cell, which is the bottom of the floats.
Now, the a element in the first cell is a non-replaced inline element. Its height rules say
The vertical padding, border and margin of an inline, non-replaced box
start at the top and bottom of the content area, and has nothing to do
with the 'line-height'. But only the 'line-height' is used when
calculating the height of the line box.
... And the height of the first table-cell is the height of its stack of line boxes, which, in this case, is the line-height of the one and only line it contains. So the a elements padding and border don't affect the height of the table cell and in fact escape the first table cell, which you can see if you add a background-color to the cells
The resolution, as has already been pointed out, is to add vertical-align:middle to the first cell.
Related
div {
background:red;
}
<div><span style="display: inline-block"></span></div>
The w3c send
On a block container element whose content is composed of inline-level elements, 'line-height' specifies the minimal height of line boxes within the element. The minimum height consists of a minimum height above the baseline and a minimum depth below it, exactly as if each line box starts with a zero-width inline box with the element's font and line height properties. We call that imaginary box a "strut." (The name is inspired by TeX.).
https://www.w3.org/TR/CSS2/visudet.html#inline-box-height
But when the span display is 'inline', why the div's height are 0?
I am confused?
But when the span display is 'inline', why the div's height are 0?
Not 100% correct because if the span has at least one character the height will be different from 0. Even an invisible zero width space:
div {
background: red;
}
<div><span></span></div>
In case of an empty span (having display:inline) the browser will generate 0 line box. So inside your div there is no line box thus you have a height equal to 0.
Adding one character will trigger the creating of one line box and the rule you quoted will be used and the line-height will define the height of the line box and the height of a div is the height of its line box (since we only have one).
Same logic if you add an empty inline-block element. Even empty, an inline-block will trigger the creation of a line box.
Same logic if you use an empty img:
div {
background: red;
}
<div><img></div>
To use easy words, an inline-block element is still considered as an existing element and we need to generate a line box to hold it. An empty inline element will be considered by the browser as a non-existing element so we don't need any line box to hold something that doesn't really exist.
Line boxes are created as needed to hold inline-level content within an inline formatting context. Line boxes that contain no text, no preserved white space, no inline elements with non-zero margins, padding, or borders, and no other in-flow content (such as images, inline blocks or inline tables), and do not end with a preserved newline must be treated as zero-height line boxes for the purposes of determining the positions of any elements inside of them, and must be treated as not existing for any other purpose. ref
line-height only applies when there are actual lines inside an element, in your case, <span>.
Since you did not fill the element with any text, the div does not expand.
If you want to leave the span empty, try specifying height or adding margin to the span, like this:
div {
background: red;
}
span {
display: inline-block;
margin: 1em;
}
<div>
<span></span>
</div>
This question already has answers here:
Image inside div has extra space below the image
(10 answers)
Closed 7 years ago.
I just changed the header image on my site from
<div style="background-image... width=1980 height=350>
to using
<img src="... style="width:100%;">
so the image would scale down which it now does...
But now I have this mysterious 10px gap or so.
I've checked the inspector in Chrome, and I just can't see what's causing the space. I've searched other posts but can't find anything that applies.
Anyone out there have any idea? Appreciate any help, Bob :)
Look at this line of text. Notice there are no letters that breach the baseline.
Now look at the following sentence:
By just crossing the bridge he probably got away.
Note the letters j, g, p and y. These letters, known in typography as descenders, breach the baseline.
Source: Wikipedia.org
The default value of the vertical-align property is baseline. This applies to inline-level elements.
Your img is inline-level by default and, like text, span, input, textarea and other inline boxes, is aligned to the baseline. This allows browsers to provide the space necessary to accommodate descenders.
Note that the gap is not created by margin or padding, so it's not easy to detect in developer tools. It's a slight elevation of content from the container's bottom edge resulting from baseline alignment.
Here are several ways to handle this:
Apply vertical-align: bottom to the img tag. In some cases bottom won't work, so try middle, top or text-bottom.
Switch from display: inline to display: block.
Adjust the line-height property on the container. In your code reference (since removed due to linkrot), line-height: 0 did the trick.
Set a font-size: 0 on the container. You can restore the font-size on the child element directly, if necessary.
Related:
Why is my textarea higher up than its neighbor?
By default, IMG is an inline element. You need to set your IMG tag to be a block element, which can be accomplished with this style:
display: block;
Add
display: block;
to the <img>.
Consider this HTML:
<div id="x">Foo</div>
<div id="y">Bar <span>Baz</span></div>
And this CSS:
#x {
background: orange;
display: inline;
font-size: 200%;
}
#y {
background: cyan;
display: inline;
vertical-align: top;
}
Here is the JSFiddle demo: http://jsfiddle.net/ePBZz/
With Firefox 28.0 and Internet Explorer 8, both 'Bar' and 'Baz' appear on the same line. However, with Chrome 33.0, they do not appear on the same line.
(Output with Firefox on left; Output with Chrome on right)
From the 'Inspect element' feature of Firefox and Chrome I figure that in both cases the span element obeys the standard of using vertical-align: baseline (the default). So Chrome must be using a different definition of baseline for the span element than Firefox when vertical-align: top is used for the parent element, otherwise I do not know how to explain the difference in the output between Chrome and Firefox.
By the way, I know how to fix this code. By adding the following to the CSS.
#y span {
vertical-align: inherit;
}
This would force the child span element to inherit vertical-align: top from the parent #y element. However, that is not my question.
I want to know which one is the buggy behaviour and which one is the correct behaviour as per the W3C CSS standard for my code without the fix I mentiond later, or if the result of this kind of code is unspecified by the standard. If possible, please quote the sections from the W3C documents that clarifies the behaviour for such code.
After reading CSS 2.1 specification (W3C Recommendation 07 June 2011) it seems to me that both Chrome and Firefox are correct and they are free to choose different baselines.
Section 9.2.2. defines what an inline-level box is.
9.2.2 Inline-level elements and inline boxes
Inline-level elements are those elements of the source document that
do not form new blocks of content; the content is distributed in lines
(e.g., emphasized pieces of text within a paragraph, inline images,
etc.). The following values of the 'display' property make an element
inline-level: 'inline', 'inline-table', and 'inline-block'.
Inline-level elements generate inline-level boxes, which are boxes
that participate in an inline formatting context.
An inline box is one that is both inline-level and whose contents
participate in its containing inline formatting context. A
non-replaced element with a 'display' value of 'inline' generates an
inline box. Inline-level boxes that are not inline boxes (such as
replaced inline-level elements, inline-block elements, and
inline-table elements) are called atomic inline-level boxes because
they participate in their inline formatting context as a single opaque
box.
Section 9.4.2 defines what a line box is.
9.4.2 Inline formatting contexts
In an inline formatting context, boxes are laid out horizontally, one
after the other, beginning at the top of a containing block.
Horizontal margins, borders, and padding are respected between these
boxes. The boxes may be aligned vertically in different ways: their
bottoms or tops may be aligned, or the baselines of text within them
may be aligned. The rectangular area that contains the boxes that form
a line is called a line box.
Then section 10.8 mentions:
The inline-level boxes are aligned vertically according to their
'vertical-align' property. In case they are aligned 'top' or 'bottom',
they must be aligned so as to minimize the line box height. If such
boxes are tall enough, there are multiple solutions and CSS 2.1 does
not define the position of the line box's baseline (i.e., the position
of the strut, see below).
This seems to apply to the code at http://jsfiddle.net/ePBZz/. In this code, vertical-align: top has been applied to the div id="y" which has been defined as follows.
<div id="y">Bar <span>Baz</span></div>
There are two inline-level boxes in this element. The two inline-level boxes are:
Anonymous: "Bar"
SPAN: "Baz"
Now it is not clear what it really means for inline-level boxes to be tall enough, but if we assume that these two inline-level boxes are tall enough, then as per the last quoted text, the standard does not define the baseline of the line box that contains these two inline-level boxes. As a result, Chrome and Firefox are free to choose different baselines and align the baseline of the second inline level-box (SPAN: "Baz") with these differently chosen baselines, thereby producing different outputs.
I used text-align: center to to position three divs. But there is a small gap between each and every div. Why is it so? the picture is giving below? The divs are displayed as inline-block.
Inline-block elements often have spaces in between them because HTML displays newlines in the code as a space character.
For example, this will have a space between each div:
<div>blah</div>
<div>blah...</div>
<div>blahblah...</div>
There are various workarounds for this such as getting rid of the space in your code:
<div>blah</div><div>blah...</div><div>blahblah...</div>
Or setting the parent element to font-size: 0 and then setting the child divs to whatever font size you want.
I personally thought this was an interesting post on the subject: http://css-tricks.com/fighting-the-space-between-inline-block-elements/
If the div elements are inline-block display, then the reason there are spaces in-between them is because it is recognizing all the new lines and spaces between the div elements and trimming them down to one space character. That is the space you are seeing.
You can solve this by using float: left; if that is applicable to your situation. Of course, you may have to confine them to their own block formatting context due to the floats.
Another solution would be to get rid of the new lines and spaces in-between the div elements. You can do that like so:
HTML:
<div><img src="picture.jpg"></div
><div><img src="picture.jpg"></div
><div><img src="picture.jpg"></div>
Unless you absolutely have to use display: inline-block; then refer to the link at the bottom of my answer for a wide range of solutions.
The best solution would be to change display: inline-block; to float: left; since they will float right next to each other by default.
If they are inline-block you will need to add margin-right: -4px to offset the default margin-right.
This is based from the lack of HTML/CSS from your question.
Here are a few options of dealing with inline-blocks default margin, CSS-Tricks Inline-block
Heres the fiddle
When I set #two to inline-block it subtracts the 16 px of top/bottom margin from the <p> and adds it to the divs content box height so it becomes 52px instead of 20px .. why is this the case?
What you're seeing is one of the stranger cases of margin collapsing.
If the parent and children are block elements and there's nothing (padding, a border, etc.) separating their vertical margins, then those margins will collapse. Collapsed margins are when two neighboring margins aren't added (as you might expect), but instead the larger of the two is displayed. In the parent-child case, the collapsed margin ends up outside the parent. You can read more details under the section Parent and first/last child in the above link.
Setting the parent to inline-block, or float:left;ing it or a number of other things (refer to the link for a more complete list) will stop the margins from collapsing. This leads to the behavior we're used to: the child's margin will appear inside the parent, adding to its total height, and the parent's margin will also be displayed.
To elaborate, and expand on the existing answers..
This behavior is known as collapsing margins.
8.3.1 Collapsing margins
In CSS, the adjoining margins of two or more boxes (which might or might not be siblings) can combine to form a single margin. Margins that combine this way are said to collapse, and the resulting combined margin is called a collapsed margin.
To work around this, you need to establish a new block formatting context:
9.4.1 Block formatting contexts
Floats, absolutely positioned elements, block containers (such as inline-blocks, table-cells, and table-captions) that are not block boxes, and block boxes with 'overflow' other than 'visible' (except when that value has been propagated to the viewport) establish new block formatting contexts for their contents.
In a block formatting context, boxes are laid out one after the other, vertically, beginning at the top of a containing block. The vertical distance between two sibling boxes is determined by the 'margin' properties. Vertical margins between adjacent block-level boxes in a block formatting context collapse.
Therefore a few different ways to establish a new block formatting would be to..
Change the overflow property of the parent element to something other than the default value, visible - (updated example)
Change the display of the element to inline-block - (updated example)
Float the element - (updated example)
This has already been answered and accepted, still I'd like to point out that clearfixing it would have prevented margin collapse thus normalizing its behaviour
I'd add:
.two:before,
.two:after {
content: " ";
display: table;
}
.two:after {
clear: both;
}
See this fiddle .
Here's the Nicholas Gallagher clearfix I've used.
Paragraphs have margins built in (in most browsers).
Try this:
p
{
margin: 0px;
}