Roughly speaking, attempting to build a four-column layout, I've got this HTML:
<div>
<div>A column</div>
<div>A column</div>
<div>A column</div>
<div>A column</div>
</div>
And I've got this CSS:
div {
background: #ccc;
}
div div {
background: #eee;
display: inline-block;
width: 25%;
}
-> Fiddle me this <-
When rendered in the browser (Currently, I have been testing with Chrome only) the whitespace between the nested div elements (in this example the whitespace is caused by line breaks) is rendered, thus throwing my layout out.
Clearly, I can float my nested divs...
div {
background: #ccc;
}
div div {
background: #eee;
width: 25%;
float: left;
}
-> Fiddle me that <-
But then my container div collapses and I don't want to have to have to use CSS clearfix hacks or extra HTML to open it back up.
Alternatively I can modify my HTML such that the whitespace is removed...
<div><div>A column</div><div>A column</div><div>A column</div><div>A column</div></div>
but that makes it hard to work with. The alternative of breaking the tags so that it becomes more readable somehow leaves me feeling dirty...
<div>
<div>A column</
div><div>A column</
div><div>A column</
div><div>A column</div>
</div>
I've found a resource or two (I failed to find anything on SO) but I don't really like any of the solutions - they are all workarounds, which I will entertain if I must but surely there's an alternative?
So my question(s)... is there a cross-browser, w3c-compliant, non-javascript, hack-free, tidy HTML, bombproof way of preventing HTML whitespace from being rendered in the browser whilst using display:inline-block? Or is there an alternative to inline-block that can be used that has no unpleasant side effects?
EDIT
Assuming that this is genuinely impossible, the best solution would be something that required no addition HTML markup and 'flexible' CSS. In other words, a webmaster could edit the HTML as normal without consideration of breaking the layout, and the CSS (hacked or otherwise) will accommodate the webmaster's amends without having to be amended itself.
MY "WORKAROUND"
Well, it looks like something's got to give. In my situation it is more important to have HTML that doesn't require extra markup so the best solution is to work in a CSS hack that "just works" invisibly. The solution is to float the nested divs and add a hack...
div div {
float: left;
}
div::before,
div::after {
content: "";
display: table;
}
div::after {
clear: both;
}
div {
*zoom: 1;
}
...which is a derivation of a fix I've been using for some time and was hoping to avoid. This succint version of the fix was found on this site.
So now every single div in the markup has got the clearfix hack applied to it whether it needs it or not. I'm yet to learn if this has any bad side-effects by being applied to all divs - I look forward to debugging and fixing when any problems surface ;-)
You provided nearly all possible solutions to this big layout question. I just want to point out my preferred solution.
Set font-size to the parent to 0 and resetting it again with REM's.
You'll have no trouble with your code and layout if there is no additional text inside the parent div (not the child divs).
REM's (Relative EM's) are not relative to the font-size of the parent elements (like normal EM's are), but relative to the root element of your document – the html element.
HTML:
<div class="parent">
<div class="child">column 1</div>
<div class="child">column 2</div>
<div class="child">column 3</div>
<div class="child">column 4</div>
</div>
CSS:
html {
font-size: 1em;
}
.parent {
font-size: 0;
}
.child {
display: inline-block;
font-size: 16px; /* Add pixel-based font-size to support IE8 and below */
font-size: 1rem; /* Don't use rem along with the font-shorthand to avoid problems in IE9/10 - see note below */
width: 25%;
}
No Browser support:
IE8 and below: Add pixel-based font-size to make it work.
IE9/10: not working with font-shorthand; use font-size instead!
(Opera Mini & iOS 3.2)
is there a ... way of preventing HTML whitespace from being rendered in the browser whilst using display:inline-block?
Yes, there are several ways. None of them really meet your criteria of 'hack-free' and 'tidy', but they do work.
Reformat ('minify') your code so that it doesn't have any white space between the elements.
This is probably the most hack-free and cross-browser solution. It isn't necessarily tidy though, and it means you're fixing your layout by adjusting the HTML rather than the CSS, which isn't ideal. But it does work well. If you want to keep your code readable, you could use HTML comments so you can keep the gaps but without them being in the DOM:
<div>block 1</div><!--
--><div>block 2</div><!--
--><div>block 3</div>
Still not ideal, but more readable than a massive single line of code.
Set the font-size to zero for the container, and back to full size again for the blocks.
This works really well. It's a pure CSS solution and easy to do. The down side is that it can be difficult to work with if you've got relative font sizes (ie setting back to 14px is fine, but setting to 1em won't work because 1em of the previous font size of zero is still zero).
Set a 1em negative margin to close the gap.
This also works pretty well, but can be imprecise.
Or is there an alternative to inline-block that can be used that has no unpleasant side effects?
There's always float:left. But that's got a whole range of different issues of its own. If you're using inline-block, the odds are good it's because you don't want to use floats.
Use position:absolute and do the layout manually.
You can use the float method you described in your question, but you didn't clear your floats, which is why the container collapses.
A good method is to use an ::after pseudo element attache to the container element to "auto-clear" itself:
div:after {
content: "";
display: table;
clear: both;
}
http://jsfiddle.net/s2rJW/3/
When i saw your "workaround" i was thinking: Why don't you use a <table>?
And then i figured this out:
div {
background: #ccc;
display: table;
width: 100%;
}
div div {
background: #eee;
display: table-cell;
width: 25%
}
<div>
<div>A column</div>
<div>A column</div>
<div>A column</div>
<div>A column</div>
</div>
Related
This question already has answers here:
What methods of ‘clearfix’ can I use?
(29 answers)
Closed 8 years ago.
Although elements like <div>s normally grow to fit their contents, using the float property can cause a startling problem for CSS newbies: If floated elements have non-floated parent elements, the parent will collapse.
For example:
<div>
<div style="float: left;">Div 1</div>
<div style="float: left;">Div 2</div>
</div>
The parent div in this example will not expand to contain its floated children - it will appear to have height: 0.
How do you solve this problem?
I would like to create an exhaustive list of solutions here. If you're aware of cross-browser compatibility issues, please point them out.
Solution 1
Float the parent.
<div style="float: left;">
<div style="float: left;">Div 1</div>
<div style="float: left;">Div 2</div>
</div>
Pros: Semantic code.
Cons: You may not always want the parent floated. Even if you do, do you float the parents' parent, and so on? Must you float every ancestor element?
Solution 2
Give the parent an explicit height.
<div style="height: 300px;">
<div style="float: left;">Div 1</div>
<div style="float: left;">Div 2</div>
</div>
Pros: Semantic code.
Cons: Not flexible - if the content changes or the browser is resized, the layout will break.
Solution 3
Append a "spacer" element inside the parent element, like this:
<div>
<div style="float: left;">Div 1</div>
<div style="float: left;">Div 2</div>
<div class="spacer" style="clear: both;"></div>
</div>
Pros: Straightforward to code.
Cons: Not semantic; the spacer div exists only as a layout hack.
Solution 4
Set parent to overflow: auto.
<div style="overflow: auto;">
<div style="float: left;">Div 1</div>
<div style="float: left;">Div 2</div>
</div>
Pros: Doesn't require extra div.
Cons: Seems like a hack - that's not the overflow property's stated purpose.
Comments? Other suggestions?
Solution 1:
The most reliable and unobtrusive method appears to be this:
Demo: http://jsfiddle.net/SO_AMK/wXaEH/
HTML:
<div class="clearfix">
<div style="float: left;">Div 1</div>
<div style="float: left;">Div 2</div>
</div>
CSS:
.clearfix::after {
content: " ";
display: block;
height: 0;
clear: both;
}
With a little CSS targeting, you don't even need to add a class to the parent DIV.
This solution is backward compatible with IE8 so you don't need to worry about older browsers failing.
Solution 2:
An adaptation of solution 1 has been suggested and is as follows:
Demo: http://jsfiddle.net/wXaEH/162/
HTML:
<div class="clearfix">
<div style="float: left;">Div 1</div>
<div style="float: left;">Div 2</div>
</div>
CSS:
.clearfix::after {
content: " ";
display: block;
height: 0;
clear: both;
*zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML += '<div class="ie7-clear"></div>' );
}
.ie7-clear {
display: block;
clear: both;
}
This solution appears to be backward compatible to IE5.5 but is untested.
Solution 3:
It's also possible to set display: inline-block; and width: 100%; to emulate a normal block element while not collapsing.
Demo: http://jsfiddle.net/SO_AMK/ae5ey/
CSS:
.clearfix {
display: inline-block;
width: 100%;
}
This solution should be backward compatible with IE5.5 but has only been tested in IE6.
I usually use the overflow: auto trick; although that's not, strictly speaking, the intended use for overflow, it is kinda related - enough to make it easy to remember, certainly. The meaning of float: left itself has been extended for various uses more significantly than overflow is in this example, IMO.
Rather than putting overflow:auto on the parent, put overflow:hidden
The first CSS I write for any webpage is always:
div {
overflow:hidden;
}
Then I never have to worry about it.
The problem happens when a floated element is within a container box, that element does not automatically force the container’s height adjust to the floated element. When an element is floated, its parent no longer contains it because the float is removed from the flow. You can use 2 methods to fix it:
{ clear: both; }
clearfix
Once you understand what is happening, use the method below to “clearfix” it.
.clearfix:after {
content: ".";
display: block;
clear: both;
visibility: hidden;
line-height: 0;
height: 0;
}
.clearfix {
display: inline-block;
}
html[xmlns] .clearfix {
display: block;
}
* html .clearfix {
height: 1%;
}
Demonstration :)
There are several versions of the clearfix, with Nicolas Gallagher and Thierry Koblentz as key authors.
If you want support for older browsers, it's best to use this clearfix :
.clearfix:before, .clearfix:after {
content: "";
display: table;
}
.clearfix:after {
clear: both;
}
.clearfix {
*zoom: 1;
}
In SCSS, you should use the following technique :
%clearfix {
&:before, &:after {
content:" ";
display:table;
}
&:after {
clear:both;
}
& {
*zoom:1;
}
}
#clearfixedelement {
#extend %clearfix;
}
If you don't care about support for older browsers, there's a shorter version :
.clearfix:after {
content:"";
display:table;
clear:both;
}
Although the code isn't perfectly semantic, I think it's more straightforward to have what I call a "clearing div" at the bottom of every container with floats in it. In fact, I've included the following style rule in my reset block for every project:
.clear
{
clear: both;
}
If you're styling for IE6 (god help you), you might want to give this rule a 0px line-height and height as well.
The ideal solution would be to use inline-block for the columns instead of floating. I think the browser support is pretty good if you follow (a) apply inline-block only to elements that are normally inline (eg span); and (b) add -moz-inline-box for Firefox.
Check your page in FF2 as well because I had a ton of problems when nesting certain elements (surprisingly, this is the one case where IE performs much better than FF).
Strange no one has come up with a complete answer for this yet, ah well here it is.
Solution one: clear: both
Adding a block element with the style clear:both; onto it will clear the floats past that point and stop the parent of that element from collapsing. http://jsfiddle.net/TVD2X/1/
Pros: Allows you to clear an element and elements you add below will not be effected by the floated elements above and valid css.
Cons: Requires the another tag to clear the floats, bloating markup.
Note: To fall back to IE6 and for it to work on abstinent parents (i.e. the input element) you are not able to use :after.
Solution two: display: table
Adding display:table; to the parent to make it shrug off the floats and display with the correct height. http://jsfiddle.net/h9GAZ/1/
Pros: No extra markup and is a lot neater. Works in IE6+
Cons: Requires invalid css to make sure everything plays nice in IE6 and 7.
Note: The IE6 and 7 width auto is used to prevent the width being 100%+padding, which is not the case in newer browsers.
A note on the other "solutions"
These fixes work back to the lowest supported browser, over 1% usage globally (IE6), which means using :after does not cut it.
Overflow hidden does show the content but does not prevent the element from collapsing and so does not answer the question. Using an inline block can have buggy results, children having strange margins and so on, table is much better.
Setting the height does "prevent" the collapse but it is not a proper fix.
Invalid css
Invalid css never hurt anyone, in fact, it is now the norm. Using browser prefixes is just as invalid as using browser specific hacks and doesn't impact the end user what so ever.
In conclusion
I use both of the above solutions to make elements react correctly and play nicely with each other, I implore you to do the same.
I use 2 and 4 where applicable (i.e. when I know the content's height or if overflowing doesn't harm). Anywhere else, I go with solution 3. By the way, your first solution has no advantage over 3 (that I can spot) because it isn't any more semantic since it uses the same dummy element.
By the way, I wouldn't be concerned about the fourth solution being a hack. Hacks in CSS would only be harmful if their underlying behaviour is subject to reinterpretation or other change. This way, your hack wouldn't be guaranteed to work. However in this case, your hack relies on the exact behaviour that overflow: auto is meant to have. No harm in hitching a free ride.
My favourite method is using a clearfix class for parent element
.clearfix:after {
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
.clearfix {
display: inline-block;
}
* html .clearfix {
height: 1%;
}
.clearfix {
display: block;
}
One of the most well known solutions is a variation of your solution number 3 that uses a pseudo element instead of a non-semantic html element.
It goes something like this...
.cf:after {
content: " ";
display: block;
visibility: hidden;
height: 0;
clear: both;
}
You place that in your stylesheet, and all you need is to add the class 'cf' to the element containing the floats.
What I use is another variation which comes from Nicolas Gallagher.
It does the same thing, but it's shorter, looks neater, and maybe used to accomplish another thing that's pretty useful - preventing the child elements' margins from collapsing with it's parents' (but for that you do need something else - read more about it here http://nicolasgallagher.com/micro-clearfix-hack/ ).
.cf:after {
content: " ";
display: table;
clear: float;
}
add this in the parent div at the bottom
<div style="clear:both"></div>
The main problem you may find with changing overflow to auto or hidden is that everything can become scrollable with the middle mouse buttom and a user can mess up the entire site layout.
Another possible solution which I think is more semantically correct is to change the floated inner elements to be 'display: inline'. This example and what I was working on when I came across this page both use floated divs in much exactly the same way that a span would be used. Instead of using divs, switch to span, or if you are using another element which is by default 'display: block' instead of 'display: inline' then change it to be 'display: inline'. I believe this is the 100% semantically correct solution.
Solution 1, floating the parent, is essentially to change the entire document to be floated.
Solution 2, setting an explicit height, is like drawing a box and saying I want to put a picture here, i.e. use this if you are doing an img tag.
Solution 3, adding a spacer to clear float, is like adding an extra line below your content and will mess with surrounding elements too. If you use this approach you probably want to set the div to be height: 0px.
Solution 4, overflow: auto, is acknowledging that you don't know how to lay out the document and you are admitting that you don't know what to do.
I believe that best way is to set clear:both to the upcoming element.
Here's why:
1) :after selector is not supported in IE6/7 and buggy in FF3, however,
if you care only about IE8+ and FF3.5+ clearing with :after is probably best for you...
2) overflow is supposed to do something else so this hack isn't reliable enough.
Note to author: there is nothing hacky on clearing... Clearing means to skip the floating fields. CLEAR is with us since HTML3 (who knows, maybe even longer) http://www.w3.org/MarkUp/html3/deflists.html , maybe they should chose a bit different name like page: new, but thats just a detail...
For a webpage grid-layout I decided to use Flexbox. Now I wanted to implement some "auto-functionality", so that grid-boxes can later be inserted without the need to add classes or styles in the HTML. One of this features is to make a box allways be 75% as tall as it is wide - even if the box is resized by, for example, browserwindow resize. Off course, if the boxes content extends the 75%-height, it should (and only then should) increase its height to fit the content. I searched for hours to find a suitable solution, but I finally got it working. So I thought at least, until I added content to the box.
The auto aspect-ratio works fine, as long as the box is empty. If I add content, the 75% of the width is allways added to the height it has through extension by its content. I made a jsfiddle to clearly visualize the problem:
JSFiddle wd5s9vq0, visualizing the following Code:
HTML-Code:
<div class="container">
<div class="content-cell"></div>
<div class="content-cell"></div>
</div>
<div class="container">
<div class="content-cell">
This cell has an inreased height because of
it's content. The empty space below the
content is the 75% of the cells width.
</div>
<div class="content-cell"></div>
</div>
CSS:
.container {
display: flex;
width: 400px;
}
.content-cell {
flex: 1 1 0;
margin: 10px;
background-color: #ccc;
}
.content-cell::after {
content: "";
display: block;
padding-top: 75%;
}
If I didn't knew it better, it looks like a floating-problem - but I think the ::before / ::after selector should add the block-element before the element it is used on and not inside it.
Does anyone has an idea on how to fix this problem?
This seems to be a very widespread problem on the internet, and most solutions you find are either about wrapping the content, absolute-positioning the content or a mixture of both. This has numerous and case-dependent downsides. After hours of playing around with the code, I finally found a combination of CSS proporties that work without the need to add any DOM or make the content absolute-positioned. This looks quit basic, and I am wondering why it took me so long and why you can't find it out there on the web.
The HTML:
<div class="mybox aspect-full">
This is text, that would normally extend the box downwards.
It is long, but not so long that it extends the intended aspect-ratio.
</div>
The CSS:
.mybox {
width: 200px;
}
.aspect-full::before {
content: '';
display: block;
padding-top: 100%;
float: left;
}
The only downside I could find is that the content of your cell must float. If you use clear on one of your child objects, it is positioned below the expander-block and you are back to the original problem. If you need to clear the floating of divs inside of these aspect-ratio-cells, you might consider to wrap them and keep the wrapper floatable.
I'm not entirely sure where I have got this from but as a rule of habbit if I have a clear div after some floated elements i do the following.
<div class='clear'></div>
Then in the css
.clear{ clear:both; width 100% }
For the most part this has seen me through many years as a developer, that is until today.
Today I find out that the 100% width bit was breaking a layout in IE7, removing the width fixed it.
My question is a simple one: Are there any reasons why a width is required on a clear div?
There's a better way to clear after floated elements that doesnt require an extra HTML element. As a rule, you want to keep HTML as intuitive and semantic as possible, and leave all design and visual aids to CSS.
If I have the following HTML:
<div class="container">
<div class="floated-left"></div>
<div class="floated-right">
</div></div>
<div class="some-non-floating-content"></div>
CSS:
overflow approach:
.container {
overflow: auto;
width: 100%;
}
:after approach:
.container:after {
content: '';
display: block;
height: 0;
clear: both;
}
You can read more about these more semantic approaches here: http://css-tricks.com/all-about-floats/ and here http://www.positioniseverything.net/easyclearing.htmls
To answer your specific question about the width, I only know of it being needed when an overflow is applied to ensure the container doesn't collapse.
I always used clear: both or only left/right to avoid extra spaces on IE (example).
So, if always worked for me, in all browsers, AFAIK there is no general reason for the width: 100%.
I saw a similar question here, and did not see an answer. I'm having an issue where an element is floated right, inside a parent div, and it's causing the div to stretch the entire width of the page in IE7. This does not happen in any other browsers (Firefox and Chrome). I've also posted pictures after the question, for reference. The HTML I'm using is below:
<div id="journal" class="journalIE">
<div class="title_bar">
<div>
Testing
</div>
<div class="actions"></div>
<div class="clear"></div>
</div>
</div>
The CSS I'm using for these tags is below as well. One thing I noticed consistent between the other person's question referenced above, and my issue, is that both parent div's have positioning applied (person above has absolute, I have fixed).
#journal
{
z-index: 1;
}
.journalIE
{
right: 1px;
bottom: 18px;
position: fixed;
}
#journal .title_bar
{
background: #F3F3F3;
border: 1px solid #C5D6E8;
color: #363638;
font-size: 11pt;
font-weight: bold;
height: 20px;
padding: 4px;
margin-bottom: 4px;
}
#journal .title_bar .actions
{
float: right;
}
.clear
{
clear: both;
}
Notice that the 'actions' class is floated right. If I take away that float, my box looks like this. But with the float added, it stretches the entire screen, and looks like this. Is this a known IE bug, because it's not happening in any other browser, and it's driving me crazy.
For those wondering, I did have content in the 'actions' div, but have stripped away everything down to the root problem.
Any assistance would be greatly appreciated. Thanks very much.
You need a width: *A floated box must have an explicit width (assigned via the 'width' property, or its intrinsic width in the case of replaced elements). *
via: W3C
Do this
<div id="journal" class="journalIE">
<div class="title_bar">
<div class="Test">
Testing
</div>
<div class="actions"></div>
<div class="clear"></div>
</div>
and then add a Css class
.Test
{
float:right;
}
should do it, let us know if it does not work.
MNK
I'm not entirely sure what you want, as you didn't explain what you wanted to do with the "actions" div, but if you wanted the "actions" div to float right next to the "Testing" div, I just tried making a separate .floatr class, or it will also work if you just apply style directly to div.
.floatr {
float: right;
}
with .floatr class, apply that to "actions" div:
<div class="actions floatr"></div>
I don't know why, but it seems to me that "actions" div is ignoring the float setting in the class you set in that manner. I personally prefer to apply multiple classes to divs, which allows me to reuse that class over other divs for which I want that effect, but I've heard that some browsers will ignore any classes declared after the first one. Oh well, I haven't run into that problem yet with major browsers...
Oh wait.
I looked over code again, and I think you just had a problem with how you set your classes. Your "actions" div was missing out on the action, try adding a comma to CSS:
#journal .title_bar, .actions
{
float: right;
}
I guess sometimes to figure something out you gotta apply effect directly to make sure it can behave in the manner you expect it to, and then probably figure it's some sorta syntax error if it does work. heh.
This question already has answers here:
How can I reorder my divs using only CSS?
(27 answers)
Closed 6 years ago.
Given that the HTML
<div>
<div id="content1"> content 1</div>
<div id="content2"> content 2</div>
<div id="content3"> content 3</div>
</div>
render as
content 1
content 2
content 3
My question:
Is there a way to render it as below by using CSS only without changing the HTML part.
content 1
content 3
content 2
This can be done in browsers that support the CSS3 flexbox concept, particularly the property flexbox-order.
See here
However, support for this is only in current versions of most browsers still.
Edit Time moves on and the flexbox support improves..
This works for me:
http://tanalin.com/en/articles/css-block-order/
Example from this page:
HTML
<div id="example">
<div id="block-1">First</div>
<div id="block-2">Second</div>
<div id="block-3">Third</div>
</div>
CSS
#example {display: table; width: 100%; }
#block-1 {display: table-footer-group; } /* Will be displayed at the bottom of the pseudo-table */
#block-2 {display: table-row-group; } /* Will be displayed in the middle */
#block-3 {display: table-header-group; } /* Will be displayed at the top */
As stated there, this should work in most browsers. Check link for more info.
It might not exactly match what you're after, but take a look at this question:
CSS positioning div above another div when not in that order in the HTML
Basically, you'd have to use Javascript for it to be reliable in any way.
This is one of the classic use-cases for absolute positioning--to change rendering from source order. You need to know the dimensions of the divs to be able to do this reliably however, and if you don't javascript is your only recourse.
I was messing around in Firefox 3 with Firebug, and came up with the following:
<div>
<div id="content_1" style="height: 40px; width: 40px; background-color: rgb(255, 0, 0); margin-bottom: 40px;">1</div>
<div id="content_2" style="width: 40px; height: 40px; background-color: rgb(0, 255, 0); float: left;">2</div>
<div id="content_3" style="width: 40px; height: 40px; background-color: rgb(0, 0, 255); margin-top: -40px;">3</div>
</div>
It's not perfect, since you need to know the heights of each container, and apply that height value to the negative top margin of the last element, and the bottom margin of the first element.
Hope it helps, nd
I got it to work by doing this:
#content2 { position:relative;top:15px; }
#content3 { position:relative; top:-17px; }
but keep in mind that this will not work for you as soon as you have dynamic content. The reason I posted this example is that without knowing more specific things about your content I cannot give a better answer. However this approach ought to point you in the right direction as to using relative positioning.
One word answer: nope. Look into XSLT (XML Stylesheet Language Transforms), which is a language specifically geared towards manipulating XML.
If you know the height of each element then it is a simple case of vertical relative positioning to swap around the orders. If you don't know the heights then you either have to give them heights and allow the divs to get scroll bars if there is any overflow or calculate it all with JavaScript and add the relative positioning on-the-fly.
with jquery you can simply do:
$('#content2').insertAfter($('#content3'));
I don't think there's a way to do it with CSS, except to force fixed positioning of each of the divs and stack them that way.