Consider the following HTML and CSS:
<div class="container">
<span>This text appears inside a span tag which is an inline element.</span>
<p>This text appears inside a paragraph tag which is a block element.</p>
<span>This is another inline element.</span>
</div>
.container {
overflow: hidden;
}
Based on my understanding from the following SO question, by adding overflow: hidden to the .container class will establish the div as a new Block Formatting Context.
Question
How does this affect the span and p tags inside of the div? Specifically, what does creating a new block formatting context actually do to its children elements (in this case, inline span elements and a block p element)?
Related Question
What is an example of an Inline Formatting Context getting created? Can someone provide a HTML example and explain how it is established? What does the inline formatting context actually do to its children elements?
A block formatting context is a tool to help browsers render elements. It prevents tricky cases, so it's not really visual.
For example, float elements are not in the page flow. So other elements could be under or wrap around a floating element.
.float {
float: left;
width: 50px;
height: 50px;
margin: 10px;
background: turquoise;
}
.container {
height: 100px;
margin-left: 30px;
background: tomato;
}
<div class="float"></div>
<div class="container"></div>
So what should happen if we add a overflow: hidden to the .container element? Should the float element be cut because its over it? But the floating element is not actually inside the container...
It's one of the tricky cases, so browsers declared that overflow: hidden would trigger a safe rendering mode: Block formatting context. It will prevent the container from being around floating elements or to contain elements that are floating outside of its scope.
.float {
float: left;
width: 50px;
height: 50px;
margin: 10px;
background: turquoise;
}
.container {
height: 100px;
background: tomato;
/* BFC */
overflow: hidden
}
<div class="float"></div>
<div class="container"></div>
It seems that inline formatting context is the normal way that browsers render inline elements, those that are not rendered as block formatting context.
This rendering mode allows several elements to be on the same line. Their flow is affected by the writing direction, font-size, line-height... It also can be cut to be wrapped on several lines, etc.
Related
As I found out, the best browser friendly solution for vertical aligning is the trick with a pseudo element. But it's not working if I need to use percentages with image inside another div.
Here is a fiddle of my problem.
I need to work with percentages due to responsive design.
I realize that the problem is may be caused by the "width" of the pseudo element, because when I change the width of the child element to 99%, it jumps back where it should be, but why is this necessary? I don't want to use 99% as this can cause problems (when shrinking browser window it has to be eventually changed to 98%,97%...) and image is not touching sides of it's parent element. Does anybody know the reason? Thanks.
html, body {
height: 100%;
width: 100%;
margin: 0px;
padding: 0px;
}
.parent {
width: 100%;
height: 100%;
}
.parent:before {
content: "";
display: inline-block;
height: 100%;
vertical-align: middle;
}
.child {
display: inline-block;
height: 25%;
width: 100%;
}
img {
width: 100%;
height: 100%;
}
<div class="parent">
<div class="child">
<img src="http://www.resortcollection.com/wp-content/themes/resortcollection/property-images/summit/summit-beach-resort-panama-city-beach-fl-beach-01.jpg">
</div>
</div>
The trick is to remove all whitespaces between parent and child divs, so there is no whitespace between :before and child. And also you should remember to add vertical-align to child
Example fiddle
Yes. When you put the :before pseudo-element in place it goes right at the start of the content, that is, before any spaces. So your horizontal layout is
before-pseudo single-collapsed-Space img
Zero width 4px (from the font size) 100% of container
Which is too big. Use margin-left:-4px; on the pseudo element to compensate.
See https://jsfiddle.net/xoks5f1e/5/
I will suggest my own solution that I've mysteriously did not see earlier. It is a solution that most of professional webdesigners use. And that is to remove the space/line-break between tags styled with display: inline-block.
Important note: When applying ::before pseudo element on some parent element (e.g. .parent), CSS will append this new pseudo element directly "before" the first child element of the node .parent. That means if the first child element of .parent is separated from the opening .parent tag by a new paragraph character or a space, then this white space character will also be before the newly appended pseudo element. And because of that, when using display: inline-block the white-space character will also affect CSS's pseudo element.
The solution in case you have the access to the HTML file, is:
First child of .parent must be directly after .parent's opening tag as #DenisSheremet suggested.
Make a real element instead of "pseudo" one in real DOM:
<div style="display: inline-block; height: 100%; vertical-align: middle"></div><
div class="child">
or more elegantly using comments:
<div style="display: inline-block; height: 100%; vertical-align: middle"></div><!--
--><div class="child">
If you have only access to CSS, than the only other choice what I know so far is the solution suggested by #Alohci to remove the white space with default font space width of 4px, using negative margin.
JSFiddle
I have the following problem: I am creating an inline-block element (.content) within a wrapper-div (.wrapper). If there is content in the .content-div, everything works just fine. But if I remove the content from the .content-div, a space gets added below the inline-block-div.
I am not sure why this happens and how to fix it correctly. Note that after manually removing all spaces and line-breaks in my code the problem persists, but setting the font-size to 0 helps.
Also, setting vertical-align: top to the .content-div helps. I am not sure why exactly.
Whats the best way of fixing it? Why does this happen?
Fiddle: https://jsfiddle.net/cjqvcvL3/1/
<p>Works fine:</p>
<div class="wrapper">
<div class="content">not empty</div>
</div>
<p>Not so much:</p>
<div class="wrapper">
<div class="content"></div>
</div>
.wrapper {
background-color: red;
margin-bottom: 20px;
/* font-size: 0; *//* this would fix it, but why? (problem persists after manually removing all spaces and breaks) */
}
.content {
display: inline-block;
height: 20px;
width: 200px;
background-color: green;
/* vertical-align: top; *//* this would fix it, but why? */
}
Update
I have put together a new fiddle. This should better illustrate my problem. How do I get rid of the green line below the textarea?
https://jsfiddle.net/cjqvcvL3/7/
<div class="content"><textarea>Some
Content</textarea></div>
.content {
display: inline-block;
background-color: green;
}
This happens because you specifically give width and height to the .content.
Have you considered using the :empty pseudo selector?
.content:empty {
display: none;
}
https://jsfiddle.net/cjqvcvL3/5/
Setting your the content display to block instead of inline-block fixes the problem.
.content {
display: block;
height: 20px;
width: 200px;
background-color: green;
/* vertical-align: top; *//* this fixes it */
}
This explains why setting vertical-align to top fixes the problem as well:
The vertical-align CSS property specifies the vertical alignment of an
inline or table-cell box.
Here is a working example: jsfiddle
To remove the gap, you have to surround the content div with a wrapper with font-size:0.
The reason is exained here: answer
inline-block
This value 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.
inline
This value causes an element to generate one or more inline boxes.
The most important part for this topic would be that the element itself get's formatted not just the content. Every inline-block element will be seen as atomic inline box and thus take up space.
.wrapper2 {
background-color: red;
margin-bottom: 20px;
font-size:0;
}
The content of hr tag flow around floating elements as if it is inline elements (even if it is actually blocks). That's what I need but unfortunately hr can't have child elements except two pseudo elements.
Take a look on this demo on JsFiddle: http://jsfiddle.net/P3KEZ/
<div id="right"></div>
<div class="divider"></div>
<hr class="divider" />
#right{
background: #ffaaaa;
width: 200px;
height: 300px;
float: right
}
.divider {
background: #4d9d4d;
height: 20px;
border: none;
position: relative;
}
.divider:after, .divider:before {
content: " ";
width: 20%;
height: 100%;
display: inline-block;
position: absolute;
background: #a2a2f2;
top: 0;
}
divider:before {
left: 0;
}
.divider:after {
right: 0;
}
What I actually want is to get element with content flow around the floating elements (like hr do) but also can have at least 3 child elements (like div can do).
So question is: how to emulate such behaviour in div? (without display: flex)
What I actually want is to get element with content flow around the floating elements (like hr do) but also can have at least 3 child elements (like div can do).
So question is: how to emulate such behaviour in div?
You want to harvest the power of the mighty overflow property … (*thunderclap*)
.divider {
/* … */
overflow:hidden;
}
Normally, a block element is layed out behind a floating element, only its inline content floats next to the floated element – but with overflow:hidden you can change that, so that a block element like div only takes the space that is left beside the floating element. (It does not actually have to be hidden – everything besides the default value visible will trigger this behavior, so you can use auto or scroll as well if those suit your actual use-case better.)
See here: http://jsfiddle.net/P3KEZ/1/
I'll let you see the code first then tell you what my problem is:
Tinkerbin: http://tinkerbin.com/x8iGCFsZ
<style>
div.container{
height: 200px;
width: 200px;
background-color:red;
margin-top: 10px;
}
div.subContainer{
position: relative;
text-align: center;
}
div.inner{
position: absolute;
background-color:yellow;
width: 150px;
}
</style>
<div class="container">
<div class="subContainer">
<div class="inner">bananas for breakfast</div>
</div>
</div>
So, according to the textbook, text-align: center;, when applied to a parent element, only centers its child elements if they have display: inline;.
Therefore, and as you'd expect, since a <div> has by default display set to block (display:block;) the text-align: center; applied to the parent div.subContainer doesn't do anything to its child div.inner.
Everything fine so far. Nothing weird.
My problem arouses when I try using <span>, instead of <div> on the .inner element, and I position it absolutely (position: absolute;) — which, as you know force changes the display, from its default inline, to block.
Take a look:
<style>
div.container{
height: 200px;
width: 200px;
background-color:red;
margin-top: 10px;
}
div.subContainer{
position: relative;
text-align: center;
}
span.inner{
position: absolute;
background-color:yellow;
width: 150px;
}
</style>
<div class="container">
<div class="subContainer">
<span class="inner">bananas for breakfast</span>
</div>
</div>
What happens is weird. In spite of having the forced display value of block (thanks to the position: absolute;) the span is still centered. And even more, the centering is actually weird. It takes the left side of the block and aligns it with the center of the containing element, instead of, as usual, aligning both center.
The behavior is fixed — starts acting like a block — when I manually set the display on the span.inner to block.
span.inner{
position: absolute;
display: block;
background-color:yellow;
width: 150px;
}
So, what's happening here? Does the absolutely positioning not force change the display to block? Why is the centering weird?
When you set it to position: absolute, it does become a block, but it gets removed from the flow of content at the point it would have originally appeared. Since you are not using top, left, bottom, and right, this is much more noticeable.
When using a <div>: A division, by default, is block-level and will take up the entire width possible. So it would, by default, start at the left side of the box and expand to the right. Positioning this absolutely would keep it at the top left corner where it originally appeared, the only noticeable difference being the width of the box.
When using a <span>: A span, by default, is an inline element which is affected by the text-align property of it's parent. The text cursor starts at the center of the element, and as text is entered, the characters are added and re-centered to adjust to the width of the text. When you remove the span from the flow, it starts at the point where the text would have started. The text cursor is at the very center (horizontally) of the element, since now there is no text in the actual parent.
You've already found the solution: adding display: block will force the element to actually be a block-level element. The thing is, just because position: absolute "forces" this property, it's not an actual property, only a computed value for the element. That being said, it only gets applied to the element's position when its explicitly set in its CSS definitions.
I'm trying to set up a div which contains 4 divs. I want to set the width of the container and some of the contained divs to set values but they just seem to take the width of the content.
<html>
<head>
<style>
div {
border: 1px solid #888;
}
.container {
width: 300px;
position: relative;
}
.container div {
display: inline;
}
.div1 {
width: 20px;
overflow: hidden;
}
.div2 {
width: 80px;
overflow: hidden;
}
.div3 {
width: 160px;
overflow: hidden;
}
.div4 {
width: 20px;
overflow: hidden;
position: absolute;
top:0px;
right: 0px;
}
</style>
</head>
<body>
<div class="container">
<div class="div1"><img src="1x1.gif" width="1" height="1"/></div>
<div class="div2"><span>date</span></div>
<div class="div3"><span>text</span></div>
<div class="div4"><span>twistie</span></div>
</div>
</body>
</html>
The result looks like this:
+--+----+----+------------------------+---+
| |date|text| |twi|
+--+----+----+------------------------+---+
Can anyone explain why the left-hand divs are not being set to the required widths?
The reason you can't set the widths is because you are setting display:inline;.
When elements are displayed inline, they cannot have their dimensions specified because the size of the element is determined by the length of the text within it.
By default, <div> tags are set to display:block;. This mode can have its height and width specified, but defaults to being displayed below the preceding block.
There are two ways around this for you:
Use display:block; and float:left; -- This will change the blocks into floating elements, which means that subsequent elements will wrap around them. When used with other blocks, this effectively allows you to line them up. However using float can have other unexpected side-effects, due to the wrap-around effect I described.
Use display:inline-block; -- This is my preferred solution to this question. inline-block is a half-way house mode between block and inline. It allows an element to be treated as inline for the purposes of document flow, but still behave like a block internally, in that it will always be rectanguar and you are able to specify height and width, etc. It does have a few quirks (most notably poor support in IE6), but in general for what you're trying to achieve, it's a much cleaner solution and doesn't have the odd side-effects of float.
Hope that helps.
i think it's because of display:inline style
try this:
<div style='width:100px;overflow:hidden;'>
<div style='float:left;width:20px'></div>
<div style='float:left;width:20px'></div>
<div style='float:left;width:20px'></div>
<div style='clear:both;'></div>
</div>
Change your CSS as follows
.container div {
display: inline-block;
}
When you set the container div to inline, you actively set all of its children to inline as well, you may as well have just been using <span>s.
Here is an example for you to see.
http://jsfiddle.net/Kyle_Sevenoaks/ZwKDb/
.container {
float: left;
width: 300px;
position: relative;
}
.container div {
float: left;
}
Should do the trick. Remove the inline display on the interior divs, and float all the divs left. Then you can specify the widths of the divs and any margins between them.