I try to use the following structure to generate the layout described below.
<div class="flexbox">
<div class="box box1">child 1</div>
<div class="box box2">child 2</div>
<div class="box box3">child 3</div>
<div class="box box4">child 4</div>
</div>
I made an example for that here.
The layout should be as follows:
if there is only one box inside the flexbox it should have 50% of the width inside the flexbox (see following figure)
if there are two boxes they both should take 50% of the space inside the flexbox
if there are three boxes, each box should take 50% of the space, while the first two boxes are in row 1 and the third box appears in row 2
How can I achieve this kind of layout with css?
Edit: The boxes might not have the same height. This means they should fill the remaining space vertically. The width is always the same. See the following image for an example.
Edit: I found a way to make masonry with pure css see here: http://jsfiddle.net/confile/aGXzU/
The problem is that the boxes are in the wrong order. They should be from left to right and from top to bottom like this:
1 2 3
4 5 6
7 8 9
Is there a way to get this with css and only little javascript?
Here is a new answer and hope it solves your problem , following two Fiddles , to handle this issue , the first script will throw all odd children in the left side , and all even children on the right side
side1=0,side2=0
$(".flexbox").children().each(function(index, element) {
if (index % 2 === 0) //odd children (starts with 0 )
{
$(this).css("top",side1+"px")
side1+=parseInt($(this).css("height"))
}
else //even children
{
$(this).css("top",side2+"px")
$(this).css("left","50%")
side2+=parseInt($(this).css("height"))
}
});
http://jsfiddle.net/prollygeek/QD9kZ/
while this second fiddle , will balance the two sides based on elements heights so that there is no big deviation in the columns heights all the time , use any script of them it is up to you.
side1=0,side2=0
$(".flexbox").children().each(function(index, element) {
if(side1<=side2)
{
$(this).css("top",side1+"px")
side1+=parseInt($(this).css("height"))
}
else if(side2<side1)
{
$(this).css("top",side2+"px")
$(this).css("left","50%")
side2+=parseInt($(this).css("height"))
}
});
http://jsfiddle.net/prollygeek/hP6fS/
Flexbox cannot be used to recreate the Masonry layout. Period.
Flexbox is for controlling how elements flow along either a horizontal row (flex-direction: row, which is the default) or vertical column (flex-direction: column). That means you can only eliminate excess space in one direction: left/right (row) or top/bottom (column). Because flex-direction: column requires an explicit height to enable wrapping, it is entirely unsuitable for this purpose.
The CSS Multi-column Layout Module is the closest you can get to recreating a Masonry layout using pure CSS, but it still only allows you to eliminate excess space between the elements in one direction: vertically. The key difference between this and Flexbox (using the column direction) is that the Multi-Column module does not require an explicit height and will distribute the contents equally between each of the columns as best it can (this can be controlled via the column-fill property). The gap between the columns is controlled by the column-gap property.
http://codepen.io/cimmanon/pen/CcGlE
.my-element {
-moz-columns: 15em;
-webkit-columns: 15em;
columns: 15em;
}
If there are only two columns, why not just use floats, alternating left and right floating/clearing for odd and even boxes?
.container {
background-color: yellow;
overflow: hidden;
width: 100%;
}
.box {
height: 100px;
width: 50%;
}
.box:nth-child(odd) {
float:left;
clear: left;
}
.box:nth-child(even) {
float:right;
clear: right;
}
.box1 {
background-color: lime;
}
.box2 {
background-color: blue;
height: 120px;
}
.box3 {
background-color: red;
height: 140px;
}
.box4 {
background-color: green;
}
<div class="container">
<div class="box box1">child 1</div>
<div class="box box2">child 2</div>
<div class="box box3">child 3</div>
<div class="box box4">child 4</div>
</div>
.flexbox {
background-color: yellow;
width: 100%;
height: 300px;
}
.box {
float:left;
position:realtive;
height: 100px;
width:50%;
}
try adding and removing children !!
http://jsfiddle.net/prollygeek/8hHDg/5/
For boxes of the same size:
If you're using flexbox, you'll want to use flex-direction: row; and flex-wrap: wrap;
I forked your JSFiddle. All flex-box properties I added are prefixed with -webkit- only
References:
A Complete Guide to Flexbox
How do I implement a multi-line flexbox?
For boxes of multiple sizes:
You'll need a 'masonry' layout. This article should help you with this issue. The last section is entitled 'Pure CSS'; if you want to avoid JavaScript plugins, that should be what you want.
Unfortunately, pure CSS is only able to make top-to-bottom left-to-right layouts. For left-to-right top-to-bottom layouts, you'll need JavaScript. Check out the jQuery suggestions in the article above.
If you want to do this with pure JavaScript (without jQuery), check out this masonry library.
Related
This question already has answers here:
CSS-only masonry layout
(4 answers)
Closed 4 years ago.
This is my problem:
I have different DIVs with all the same widths but different heights.
On a large viewport these DIVs should be arranged as a grid with two columns.
The margin between the DIVs should be equal (vertically and horizontally).
Since the DIVs should be displayed in one column with the correct order on mobile it is not possible to have its own parent elements for each column.
Here is an image of what I want to achieve:
Is there any way to solve this with pure html/css?
The only solution I found so far is to use some kind of masonry javascript. But I feel like there must be a better solution...
What I've tried so far:
Using float/inline-block: I get perfect rows but 4 always starts at the same height as 3. So the margins are not equal. (See: https://codepen.io/OsmaGiliath/pen/vaPqro)
// EXAMPLE I
.parent {
width:230px;
}
.children {
display:inline-block;
width:100px;
}
Flexbox: Same (See: https://codepen.io/OsmaGiliath/pen/ajMgjR)
// EXAMPLE II
.parent {
display:flex;
flex-wrap: wrap;
}
.children {
flex:none;
}
Vertical flexbox: Works – but only with a fixed height on the parent element which is not possible in my example since this would limit the elements in the growth (See: https://codepen.io/OsmaGiliath/pen/ZjPdVx)
// EXAMPLE III
.parent {
display:flex;
flex-wrap: wrap;
flex-direction:column;
}
.children {
flex:none;
}
You can add columns that will warp up in one column if there is no enough width. This will allow you to display it as one column on mobiles. See working example here: https://codepen.io/anon/pen/BPEaXQ . You can see it working by changing the width of parent "grid" element to simulate mobiles.
<div class="grid">
<div class="column">
<div class="element higher">1</div>
<div class="element">2</div>
</div>
<div class="column">
<div class="element">3</div>
<div class="element">4</div>
</div>
</div>
.grid {
display:flex;
flex-wrap:wrap;
flex-direction:row;
margin:0 auto;
width:230px;
border:1px solid blue;
}
.column {
display: flex;
flex-direction: column;
}
.element {
width:100px;
height:140px;
margin:5px;
background: red;
}
.higher {
height:160px;
}
I finally found a solution thanks to the comment by #tobias-k.
For Desktop:
Using columnt-count: 2 on the parent element
Change the order of the 2nd and 3rd element
For Mobile:
Position the elements in a column using flexbox
Use flexbox's order to swap back the 2nd and 3rd element
https://codepen.io/OsmaGiliath/pen/vaMYPY
Thank you for all the quick responses!
I have two variable-width elements that I'm trying to position the following way:
If they fit next to each other on the screen or in their common container, I want them align to the opposite sides of it (i.e. the second one aligned to right).
If they don't, I want them one above the other, but both aligned to the left.
Something as simple as:
<div class="container">
<div style="display: inline-block;">
I'm a variable width left element
</div>
<div style="display: inline-block; float:right;">
I'm right-floating if there's space
</div>
</div>
takes care of the first case, but obviously when the container is small enough for the second div to be rendered below the first one, it's still right-floating which is not what I want.
Is this even possible with pure CSS? I can't use media queries because of unknown/variable widths.
This layout and behavior is possible without media queries and pure CSS using flexbox.
HTML
<!-- horizontal alignment when two boxes fit -->
<div class="container">
<div class="box box1"><span>1</span></div>
<div class="box box2"><span>2</span></div>
</div>
<!-- vertical alignment when two boxes don't fit -->
<div class="container">
<div class="box box3"><span>1</span></div>
<div class="box box4"><span>2</span></div>
</div>
CSS
.container {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
width: 700px; /* for demo only; can be relative length, as well */
}
.box1 { width: 100px; }
.box2 { width: 150px; }
.box3 { width: 400px; }
.box4 { width: 500px; }
DEMO
NOTES:
When there is enough space to fit both variable-width elements on the same row, they are aligned at opposite ends of the container with justify-content: space-between.
When there is not enough space to fit both elements, they wrap with flex-wrap: wrap and align-left because the justify-content: space-between rule will left-align an element when it is alone on the row.
Note that flexbox is supported by all major browsers, except IE 8 & 9. Some recent browser versions, such as Safari 8 and IE10, require vendor prefixes. For a quick way to add all the prefixes you need, post your CSS in the left panel here: Autoprefixer.
I'm exploring different ways of doing grids. I'm trying to make a block of HTML that's a minimal as possible so I can let the CSS do the heavy lifting. As you can see in the code snippet, the basic idea is:
big block in the first slot on the left
every other block is the same width, but the heights aren't guaranteed to stay the same
#items {
width: 240px;
height: 300px;
overflow: hidden;
}
#items .item {
width: 50px;
height: 50px;
background-color: lightgray;
float: left;
margin: 0 10px 10px 0;
display: flex;
align-items: center;
justify-content: center;
}
#items .item:nth-child(1) {
width: 110px;
height: 110px;
}
#items .item:nth-child(2) {
height: 55px;
}
<div id="items">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
<div class="item">7</div>
<div class="item">8</div>
<div class="item">9</div>
</div>
The HTML is wonderfully minimal, but you can see the problem: 4 should be underneath 2 instead of just to the right of it. Clearing doesn't work because it clears 1 as well. The only options I found on my own were to 1) put 2/3 and 3/4 in wrapper divs, or 2) use JS to equalize the heights of 2/3 and 3/4. Neither solution is ideal. I also tried putting item 1 outside of the outside <div>, but couldn't seem to solve the problem that way either.
Is there a solution where I can turn this HTML into the grid I want?
The problem is that floats will always float if there is any room to float. In other words. Because 55 has 5 px of extra space the next object floating will float to the right of it because of the 5px remaining and the next object will float to the right of the next longest object. It's not smart enough and there is no way that I know of to get it to objectively re-order the floating objects for you such that it minimizes all the space. Your best bet if possible is to re-order the div's such that objects with same height are next to each other.
Your code is perfectly fine, bro.
Just delete this:
#items .item:nth-child(2) {
height: 55px;
}
Thats it, useful stuff too. Good luck!
I guess I have the simplest problem ever and cannot find a ready solution.
I need to make a grid with fixed widths and fixed distance between them.
I need x columns a 400px (x = total width/400), and during browser resizing I would need this grid to shrink, column by column (columns must always keep their width size and distance between them).
The content flows over all columns and should spread out over all columns.
That's why I don't like any open source grid system (Boostrap, Skeleton, etc.) they all use %width, and columns always change width on resizing.
What would be the simplest way?
Edit/Clarification:
This is how it looks without columns: http://jsfiddle.net/xjrt8qrm/16/show/
<div>See the fiddle</div>
I want it to have x columns. x is the maximum possible amount of 400px columns, depending on the users resolution. I want only one row of columns, so the content spreads like on a newspaper from top to bottom.
So it will look somehow like this on a PC: http://i.imgur.com/kmd620p.png (You can ignore the text/comments there).
It's pretty simple. The container holds the contents together. Float left will cause them to line up left to right. When the container runs out of space to hold them, they'll drop from the right to a row below one at a time. The clear div clears out the float so that it doesn't propagate to other nearby classes. Obviously, you'll have to handle padding, margins, etc as your style dictates.
If you needed newspaper like vertical layout, you could try a solution like this one
You could use media queries in this manner or even overflow:none to hide columns that didn't fit if that was your desired behavior.
Here's a simple solution:
HTML:
<div class="container">
<div class="fourhundred">
Div 1
</div>
<div class="fourhundred">
Div 2
</div>
<div class="fourhundred">
Div 3
</div>
<div class="clear"></div>
</div>
CSS:
.fourhundred {
width: 400px;
margin: 10px;
float: left;
}
.clear { clear:left }
.container { width: 100% }
This is why flexbox have been designed. Add to your container:
.container {
display: flex;
justify-content: space-between;
align-content: space-between;
width:100%;
}
as in this Fiddle
Simply used width: calc(100% / 3); you can use any value instead of 3. Divided the whole width into 3.
here is the Fiddle Demo
<div id = "main">
<div id ="sub">One
</div>
<div id ="sub">Two
</div>
<div id ="sub">Three
</div>
</div>
CSS Part
#main{
border: 2px solid black;
height:100px;
width:100%;
position: relative;
display:flex;
}
#sub{
border:1px solid red;
width: calc(100% / 3);
height: calc(100% - 40px);
padding:10px;
margin : 5px;
display:inline-block;
}
I would consider myself to be an intermediate/advanced CSS/HTML coder but I'm stumped on how to do the following scenario.. I'm starting to think it is impossible but I really want to believe it is..
Let's say the wrapper width is 1000px.
Within it is three columns. The two outside columns are the same width, this width is decided by the center column. The center column is the only one with content, just one line of text with 30px of padding on either side. So if the line of content is 100px with padding, than the other two columns would be (1000-100)/2 each..
Is there a dynamic way to have the two outside columns adjust to the varying width of the center column that is defined by its varying contents, one line of text?
Graphic of what I am trying to accomplish:
The very closest I could come up with was to use display: table; and table-cell. This creates the dynamic effect you're looking for, but I don't think you can get your desired effect without setting an explicit width to the center element.
HTML:
<div id="wrap">
<div id="left">
Left
</div>
<div id="center">
center
</div>
<div id="right">
Right
</div>
</div>
CSS:
#wrap
{
width: 1000px;
display: table;
}
#wrap div
{
display: table-cell;
border: 1px solid #000;
width: auto;
}
#center
{
padding: 0 30px;
text-align: center;
}
You can check out my attempt here, it has some buttons for you to see the different states, width on/off and add text etc. (the jQuery has nothing to do with the solution)
I think this is as close as you're going to get with pure CSS.
Good 'ole tables to the rescue:
http://jsfiddle.net/hgwdT/
Actually I think tables are the devil, but this works as you described. And so here it is using display: table-cell on the child divs, so it is functionally the same using nicer markup:
http://jsfiddle.net/XXXdB/
The center element can indeed have a dynamic width; to prevent the content from being squished, I simply added a white-space: nowrap to the p containing the text.
I also confirmed that this solution works in IE8 and FF, in addition to Chrome.
This not the most elegant solution, but it works. I wanted to go the pure CSS route, but couldn't figure it out. Nice work, jblasco and Kyle Sevenoaks, on figuring that out!
Here is my jsFiddle demo. If you don't mind using a little JavaScript though (utilizing jQuery in my example):
HTML:
<div id="wrapper">
<div class="side"></div>
<div id="middle">One line of text.</div>
<div class="side"></div>
</div>
CSS:
#wrapper {
margin: 0 auto;
width: 1000px;
}
#wrapper div {
float: left;
height: 300px;
}
.side {
background: #ddd;
}
#middle {
background: #eee;
padding: 0 30px;
text-align: center;
}
JavaScript:
var adjustSize = function(){
// Declare vars
var wrapper = $('#wrapper'),
middle = $('#middle'),
totalWidth = wrapper.width(),
middleWidth = middle.width(),
middleOuterWidth = middle.outerWidth(),
remainingWidth = totalWidth - middleOuterWidth,
sideWidth;
if(remainingWidth % 2 === 0){
// Remaining width is even, divide by two
sideWidth = remainingWidth/2;
} else {
// Remaining width is odd, add 1 to middle to prevent a half pixel
middle.width(middleWidth+1);
sideWidth = (remainingWidth-1)/2;
}
// Adjust the side width
$('.side').width(sideWidth);
}