Fluid width with equally spaced DIVs
Asked Answered
A

7

343

I have a fluid width container DIV.

Within this I have 4 DIVs all 300px x 250px...

<div id="container">
   <div class="box1"> </div>
   <div class="box2"> </div>
   <div class="box3"> </div>
   <div class="box4"> </div>
</div>

What I want to happen is box 1 to be floated left, box 4 to be floated right and box 2 and 3 to be spaced evenly between them. I want the spacing to be fluid as well so as the browser is made smaller the space becomes smaller also.

enter image description here

Aeromechanic answered 28/7, 2011 at 20:22 Comment(14)
Why not do display:inline-block; instead of float?Pyles
because IE6/IE7 only supports inline-block on inline elementsAeromechanic
Okay, wasn't sure which browsers you were going for.Pyles
I'm not too worried about IE6, it's IE7 that's preventing me from using this option on this occasion... grrrr at IE! :)Aeromechanic
How can you have 4 DIVs all 300px x 250px, and yet you want fluid width? Do you mean that .box1 and .box4 are both 300px wide, and the middle two divs should have fluid and equal width?Sublimation
@Sublimation nope, there is other fluid content on the site, these 4 boxes will contain affiliate links which are 300x250, thats why I want them to be evenly spacedAeromechanic
That makes no sense to me. I don't understand how you want the width and fluidity to work. Maybe a quick picture would help.Sublimation
@Sublimation img30.imageshack.us/img30/8350/fluid.png - hope this makes senseAeromechanic
@Lee Price: That makes perfect sense.Sublimation
The closest solution I could think of was to wrap each child .box div in another div that are 25% width. Then, center the child .box div to the wrapper. The .box divs will be spaced evenly, but the left and right div won't be right to the edge.Limiter
The problem here is that you can't evenly space with percentages or pixels since they will change with the window width. Can't use tables (not that I would) for the same reason. I think JavaScript will be the only way to do this... otherwise, @Paul Sham's solution works but you'll have some space on the outer left & right.Dottydoty
I made @Paul Sham's idea into a JSFiddle.Dottydoty
@Lee If the precise design is not absolutely critical, I would try the idea that I suggested and that Sparky672 created in JSFiddle. If it is, would you want a Javascript option?Limiter
@thirtydot: I'm very glad I was careful enough to say, "I think JavaScript will be the only way..."Dottydoty
S
444

See: http://jsfiddle.net/thirtydot/EDp8R/

  • This works in IE6+ and all modern browsers!
  • I've halved your requested dimensions just to make it easier to work with.
  • text-align: justify combined with .stretch is what's handling the positioning.
  • display:inline-block; *display:inline; zoom:1 fixes inline-block for IE6/7, see here.
  • font-size: 0; line-height: 0 fixes a minor issue in IE6.

#container {
  border: 2px dashed #444;
  height: 125px;
  text-align: justify;
  -ms-text-justify: distribute-all-lines;
  text-justify: distribute-all-lines;
  /* just for demo */
  min-width: 612px;
}

.box1,
.box2,
.box3,
.box4 {
  width: 150px;
  height: 125px;
  vertical-align: top;
  display: inline-block;
  *display: inline;
  zoom: 1
}

.stretch {
  width: 100%;
  display: inline-block;
  font-size: 0;
  line-height: 0
}

.box1,
.box3 {
  background: #ccc
}

.box2,
.box4 {
  background: #0ff
}
<div id="container">
  <div class="box1"></div>
  <div class="box2"></div>
  <div class="box3"></div>
  <div class="box4"></div>
  <span class="stretch"></span>
</div>

The extra span (.stretch) can be replaced with :after.

This still works in all the same browsers as the above solution. :after doesn't work in IE6/7, but they're using distribute-all-lines anyway, so it doesn't matter.

See: http://jsfiddle.net/thirtydot/EDp8R/3/

There's a minor downside to :after: to make the last row work perfectly in Safari, you have to be careful with the whitespace in the HTML.

Specifically, this doesn't work:

<div id="container">
    ..
    <div class="box3"></div>
    <div class="box4"></div>
</div>

And this does:

<div id="container">
    ..
    <div class="box3"></div>
    <div class="box4"></div></div>

You can use this for any arbitrary number of child divs without adding a boxN class to each one by changing

.box1, .box2, .box3, .box4 { ...

to

#container > div { ...

This selects any div that is the first child of the #container div, and no others below it. To generalize the background colors, you can use the CSS3 nth-order selector, although it's only supported in IE9+ and other modern browsers:

.box1, .box3 { ...

becomes:

#container > div:nth-child(odd) { ...

See here for a jsfiddle example.

Sublimation answered 30/7, 2011 at 1:6 Comment(27)
Isn't that clever. Never in a million years would I have thought to use justify in such a manner. Well done and +1, but it's still not sitting right with me for some reason... I don't know why, that's my problem, I guess. (This is certainly a great example of when there's actually a reason to use a <span> in place of a <div>.)Dottydoty
You just edited the question and my comment about <span> is obsolete. It's even better, IMHO. But just wondering if you could elaborate more about the reasoning for your edits.Dottydoty
I edited my answer because since I wrote it, I've seen this distribute-all-lines thing (can't remember where), which does the same thing, but for only IE6/7. distribute-all-lines does work with divs, whereas the old solution required a naturally inline element for IE6/7 such as span. This new version is indeed better.Sublimation
Regarding your notes on whitespace in Safari, I'm seeing no difference at all... the boxes wrap/unwrap to the next line regardless of whitespace. Mac version 5.06. Why not just two containers to get two rows?Dottydoty
@Sparky672: The Safari/whitespace thing happens for me on Windows 7 with Safari 5.1. Yeah, two containers for two rows works fine here. I've edited my answer (..again) to remove the multiple rows thing and to use the same kind of squares. I was trying to keep this answer in sync with this, evidently I got a little confused along the way. Thanks.Sublimation
I took me 3h to find that you should have spaces between each boxes in the html. "Justify" extends spaces between the elements and if your content is <div/><div/><div/> it does not work. You need to have <div/> <div/> <div/>.Ominous
@venimus: This can definitely be fiddly to get right. I'm sure you don't need me to tell you that :)Sublimation
just wanted to note that :) because it is hard to note if you work with a generated content (which is the common case). I was thinking to use justify for such case, but thank you to provide a working solution. saved me lots of experiments (despite the 3h debugging :D). In addition I could add a note that if you want your last row to be left aligned you should add some extra invisible boxes (to complete the row)Ominous
@venimus: I've wrote another answer using this technique: #10548917. What did you do to get rid of the extra height caused by adding invisible boxes?Sublimation
Could You maybe take a look at my question :) #10701622Powerless
I was halfway there in that I knew justify could accomplish what I wanted (evenly spaced divs in a fluid row), but for the life of me couldn't figure out the proper implementation. This is an excellent showcase of the span tag! Very clever indeed.Haemorrhage
Soon, all modern browsers will handle this natively with flexbox. Currently it's only Chrome and Opera, but get ready.Fai
Can someone explain why the .stretch is necessary?Zeeland
Is the white space always needed after each Div? I noticed it won't work with a container width set to 611px, it causes the last Div to break into the next row. I was thinking it would flush the borders if set to 600px, but noCorticosterone
@ash It's something to do with pushing the extremities of the container. Not sure actually. Can anyone give in-depth explanation ?Prophetic
Is it just me, or does the stretch add a few px to the parent containers size? How best to remove, and would it always be fixed amount?Insolate
Similar: #11590090Sublimation
This is the best cross-browser solution I've seen for this sort of layout. Thank you. I'm with ash though in that I still don't completely understand why the stretch span causes the layout to render the way it does. As far as I can tell, it essentially forces a new line, which causes the four divs before it to occupy one entire line. Another thing I'm still curious about as well is if it's possible to make the last line unjustified when the above is used on divs spanning multiple lines with an uneven amount of divs in the final row. Maybe it's impossible without JS/back-end, but just curious.Leund
@HartleySan: The .stretch/:after is needed because (typically) with justified text, the last line is not justified. Here, we do want the last line to be justified, hence the need for :after. As for your second question, I explored that a while ago in a previous answer. In that answer, JavaScript was required. If you need to support older browsers (IE8) then I believe you do need JavaScript.Sublimation
@thirtydot, thank you very much for that quick answer. After reading your answer and playing with the fiddle a bit more, it's starting to make sense. Also, I now understand why people wanted to add invisible divs at the end. Doing so seems to make the last row line up with the other rows, even if there isn't a full row of divs in the last row. Naturally, as you commented, JS or some server-side scripting is definitely required to pull that off though. Thanks again, and awesome answer.Leund
Noticed, that on FF 25 whitespace between divs matters. Without it their distribution breaks.Cabinet
I edited the fiddle to work without needing to specify a class on each child div, and therefore working for any arbitrary number of divs that fit. jsfiddle.net/EDp8R/3903Carnelian
I've found that this technique also works for divs that aren't uniform in size, which is very handy for navs and such - just remove the width rule on the boxes.Suppurate
@Insolate As far as I can see the stretch is adding quite a few pixels to the bottom of the parent container. Adding line-height:0 to the parent container reduces the space quite a bit but does not reduce it to zeroWile
@Insolate Adding line-height:0; to the parent container and vertical-align:top; to the container's pseudo element should remove the extra space completely.Aleasealeatory
Has anyone found a way to make this work with Jade? I'm not sure how to get the space to set off the justification.Spirant
Here's how to get the space for an angular ng-repeat item written in Jade: https://mcmap.net/q/37552/-how-to-create-a-space-between-angular-ng-repeated-divs-in-jade HTML: https://mcmap.net/q/37553/-how-to-add-a-space-after-ng-repeat-elementSpirant
W
158

The easiest way to do this now is with a flexbox:

http://css-tricks.com/snippets/css/a-guide-to-flexbox/

The CSS is then simply:

#container {
    display: flex;
    justify-content: space-between;
}

demo: http://jsfiddle.net/QPrk3/

However, this is currently only supported by relatively recent browsers (http://caniuse.com/flexbox). Also, the spec for flexbox layout has changed a few times, so it's possible to cover more browsers by additionally including an older syntax:

http://css-tricks.com/old-flexbox-and-new-flexbox/

http://css-tricks.com/using-flexbox/

Wine answered 15/3, 2014 at 11:52 Comment(3)
Thank you for this, so easy, I applied it to four even spaced lists in a footer fixed at the bottom of a page. Worked a treat in FF28.0, Chrome 34.0.1847.116 m and IE11.Anthodium
Flexbox is not the most supported tool across the web and it does not beat classic approach of margin and padding.Supra
For everyone looking for justifying multiple divs with not defined widht: use flex-wrap with display: flex option. It will wrap divs with dynamic width.Vanderpool
T
20

If css3 is an option, this can be done using the css calc() function.

Case 1: Justifying boxes on a single line ( FIDDLE )

Markup is simple - a bunch of divs with some container element.

CSS looks like this:

div
{
    height: 100px;
    float: left;
    background:pink;
    width: 50px;
    margin-right: calc((100% - 300px) / 5 - 1px); 
}
div:last-child
{
    margin-right:0;
}

where -1px to fix an IE9+ calc/rounding bug - see here

Case 2: Justifying boxes on multiple lines ( FIDDLE )

Here, in addition to the calc() function, media queries are necessary.

The basic idea is to set up a media query for each #columns states, where I then use calc() to work out the margin-right on each of the elements (except the ones in the last column).

This sounds like a lot of work, but if you're using LESS or SASS this can be done quite easily

(It can still be done with regular css, but then you'll have to do all the calculations manually, and then if you change your box width - you have to work out everything again)

Below is an example using LESS: (You can copy/paste this code here to play with it, [it's also the code I used to generate the above mentioned fiddle])

@min-margin: 15px;
@div-width: 150px;

@3divs: (@div-width * 3);
@4divs: (@div-width * 4);
@5divs: (@div-width * 5);
@6divs: (@div-width * 6);
@7divs: (@div-width * 7);

@3divs-width: (@3divs + @min-margin * 2);
@4divs-width: (@4divs + @min-margin * 3);
@5divs-width: (@5divs + @min-margin * 4);
@6divs-width: (@6divs + @min-margin * 5);
@7divs-width: (@7divs + @min-margin * 6);


*{margin:0;padding:0;}

.container
{
    overflow: auto;
    display: block;
    min-width: @3divs-width;
}
.container > div
{
    margin-bottom: 20px;
    width: @div-width;
    height: 100px;
    background: blue;
    float:left;
    color: #fff;
    text-align: center;
}

@media (max-width: @3divs-width) {
    .container > div {  
        margin-right: @min-margin;
    }
    .container > div:nth-child(3n) {  
        margin-right: 0;
    }
}

@media (min-width: @3divs-width) and (max-width: @4divs-width) {
    .container > div {  
        margin-right: ~"calc((100% - @{3divs})/2 - 1px)";
    }
    .container > div:nth-child(3n) {  
        margin-right: 0;
    }
}

@media (min-width: @4divs-width) and (max-width: @5divs-width) {
    .container > div {  
        margin-right: ~"calc((100% - @{4divs})/3 - 1px)";
    }
    .container > div:nth-child(4n) {  
        margin-right: 0;
    }
}

@media (min-width: @5divs-width) and (max-width: @6divs-width) {
    .container > div {  
        margin-right: ~"calc((100% - @{5divs})/4 - 1px)";
    }
    .container > div:nth-child(5n) {  
        margin-right: 0;
    }
}

@media (min-width: @6divs-width){
    .container > div {  
        margin-right: ~"calc((100% - @{6divs})/5 - 1px)";
    }
    .container > div:nth-child(6n) {  
        margin-right: 0;
    }
}

So basically you first need to decide a box-width and a minimum margin that you want between the boxes.

With that, you can work out how much space you need for each state.

Then, use calc() to calcuate the right margin, and nth-child to remove the right margin from the boxes in the final column.

The advantage of this answer over the accepted answer which uses text-align:justify is that when you have more than one row of boxes - the boxes on the final row don't get 'justified' eg: If there are 2 boxes remaining on the final row - I don't want the first box to be on the left and the next one to be on the right - but rather that the boxes follow each other in order.

Regarding browser support: This will work on IE9+,Firefox,Chrome,Safari6.0+ - (see here for more details) However i noticed that on IE9+ there's a bit of a glitch between media query states. [if someone knows how to fix this i'd really like to know :) ] <-- FIXED HERE

Turbinate answered 30/4, 2013 at 22:30 Comment(0)
T
20

Other posts have mentioned flexbox, but if more than one row of items is necessary, flexbox's space-between property fails (see the end of the post)

To date, the only clean solution for this is with the

CSS Grid Layout Module (Codepen demo)

Basically the relevant code necessary boils down to this:

ul {
  display: grid; /* (1) */
  grid-template-columns: repeat(auto-fit, 120px); /* (2) */
  grid-gap: 1rem; /* (3) */
  justify-content: space-between; /* (4) */
  align-content: flex-start; /* (5) */
}

1) Make the container element a grid container

2) Set the grid with an 'auto' amount of columns - as necessary. This is done for responsive layouts. The width of each column will be 120px. (Note the use of auto-fit (as apposed to auto-fill) which (for a 1-row layout) collapses empty tracks to 0 - allowing the items to expand to take up the remaining space. (check out this demo to see what I'm talking about) ).

3) Set gaps/gutters for the grid rows and columns - here, since want a 'space-between' layout - the gap will actually be a minimum gap because it will grow as necessary.

4) and 5) - Similar to flexbox.

body {
  margin: 0;
}
ul {
  display: grid;
  grid-template-columns: repeat(auto-fit, 120px);
  grid-gap: 1rem;
  justify-content: space-between;
  align-content: flex-start;
  
  /* boring properties: */
  list-style: none;
  width: 90vw;
  height: 90vh;
  margin: 2vh auto;
  border: 5px solid green;
  padding: 0;
  overflow: auto;
}
li {
  background: tomato;
  height: 120px;
}
<ul>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
</ul>

Codepen demo (Resize to see the effect)


Browser Support - Caniuse

Currently supported by Chrome (Blink), Firefox, Safari and Edge! ... with partial support from IE (See this post by Rachel Andrew)


NB:

Flexbox's space-between property works great for one row of items, but when applied to a flex container which wraps it's items - (with flex-wrap: wrap) - fails, because you have no control over the alignment of the last row of items; the last row will always be justified (usually not what you want)

To demonstrate:

body {
  margin: 0;
}
ul {
  
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  align-content: flex-start;
  
  list-style: none;
  width: 90vw;
  height: 90vh;
  margin: 2vh auto;
  border: 5px solid green;
  padding: 0;
  overflow: auto;
  
}
li {
  background: tomato;
  width: 110px;
  height: 80px;
  margin-bottom: 1rem;
}
<ul>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
</ul>

Codepen (Resize to see what i'm talking about)


Further reading on CSS grids:

Turbinate answered 23/3, 2017 at 14:30 Comment(3)
Very underrated answer. This was the simplest and most effective way to achieve what I wanted to do. Thank you.Midget
This was the smallest amount of CSS (and no JS!) that produced exactly the behaviour I was looking for, with some tweaking for size and space.Baudelaire
I've been looking for this for a while and finally found it!. Thanks @TurbinateYahiya
A
2

This worked for me with 5 images in diferent sizes.

  1. Create a container div
  2. An Unordered list for the images
  3. On css the unordened must be displayed vertically and without bullets
  4. Justify content of container div

This works because of justify-content:space-between, and it's on a list, displayed horizontally.

On CSS

 #container {
            display: flex;
            justify-content: space-between;
 }
    #container ul li{ display:inline; list-style-type:none;
}

On html

<div id="container"> 
  <ul>  
        <li><img src="box1.png"><li>
        <li><img src="box2.png"><li>
        <li><img src="box3.png"><li>
        <li><img src="box4.png"><li>
        <li><img src="box5.png"><li>
    </ul>
</div>
Antisana answered 24/12, 2015 at 19:46 Comment(3)
While this code may well work, a good answer would include an explanation of how it works and why it is a good solution.Determinism
It's worth noting that flexbox is not (or only partially) supported by IE caniuse.com/#feat=flexboxHouser
is this responsive?Relic
G
1

in jQuery you might target the Parent directly.

THIS IS USEFUL IF YOU DO NOT KNOW EXACTLY HOW MANY CHILDREN WILL BE ADDED DYNAMICALLY or IF YOU JUST CAN'T FIGURE OUT THEIR NUMBER.

var tWidth=0;

$('.children').each(function(i,e){
tWidth += $(e).width();

///Example: If the Children have a padding-left of 10px;..
//You could do instead:
tWidth += ($(e).width()+10);

})
$('#parent').css('width',tWidth);

This will let the parent grow horizontally as the children are beng added.

NOTE: This assumes that the '.children' have a width and Height Set

Hope that Helps.

Galengalena answered 4/6, 2013 at 11:12 Comment(0)
P
1

If you know the number of elements per "row" and the width of the container you can use a selector to add a margin to the elements you need to cause a justified look.

I had rows of three divs I wanted justified so used the:

.tile:nth-child(3n+2) { margin: 0 10px }

this allows the center div in each row to have a margin that forces the 1st and 3rd div to the outside edges of the container

Also great for other things like borders background colors etc

Pannikin answered 20/10, 2014 at 23:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.