87

I have infinite number of divs of a 100px width, which can fit into a 250px width parent. Regardless of height, I need the divs to be displayed in rows, as shown in the image. I've tried resolving this, but the div height seems to be screwing it up.

enter image description here

I'd really appreciate your help. Thanks :)

        <style>
            #holder{
            width:250px;
            border:1px dotted blue;
            display:inline-block;
        }
        .box{
            width:100px;
            height:150px;
            background-color:#CCC;
            float:left;
            text-align:center;
            font-size:45px;
            display:inline-block;
        }
        .one{
            background-color:#0F0;
            height:200px;
        }

        .two{
            background-color:#0FF;
        }

        .three{
            background-color:#00F;
        }

        .four{
            background-color:#FF0;
        }
    </style>

    <div id="holder">
        <div class="box one">1</div>
        <div class="box two">2</div>
        <div class="box three">3</div>
        <div class="box four">4</div>
    </div>

Here is the jsfiddle

Here is what I did and achieved using javascript https://jsfiddle.net/8o0nwft9/

Community
  • 1
  • 1
stevenmc
  • 1,659
  • 4
  • 18
  • 27

10 Answers10

70

To my knowledge, there's no way to fix this problem with pure CSS (that works in all common browsers):

  • Floats don't work.
  • display: inline-block doesn't work.
  • position: relative with position: absolute requires manual pixel tuning. If you're using a server-side language, and you're working with images (or something with predictable height), you can handle the pixel tuning "automatically" with server-side code.

Instead, use jQuery Masonry.

Sander Koedood
  • 6,007
  • 6
  • 25
  • 34
thirtydot
  • 224,678
  • 48
  • 389
  • 349
  • 3
    @stevenmc: No problem, but you certainly have not found a solution to *the question you asked*: `"I have infinite number of divs of a 100px width, which can fit into a 250px width parent. Regardless of height, I need the divs to be displayed in rows, as shown in the image."`. – thirtydot Mar 08 '11 at 19:23
  • 1
    Nowadays you can do it quite reasonably with CSS columns, for example: http://w3bits.com/css-masonry/ – thirtydot Aug 07 '17 at 03:15
  • 2
    CSS columns won't solve the issue completely, if the alignment should happen like numbered above. CSS columns will lead to a layout where column 1 will contain items 1,2,3 and column 2 contain items 4,5,6. – Stefan Oct 06 '17 at 07:39
  • Can the same be accomplished with flexbox? – iChido Oct 24 '18 at 19:36
  • 1
    @iChido: I don't think flexbox helps particularly with this problem, but I'm not certain. I haven't tried to do this with only CSS in a long time, I always end up using JavaScript because it just works. The Masonry library in my answer doesn't even rely on jQuery any more. Whether or not CSS alone can do it depends on your precise requirements. – thirtydot Oct 24 '18 at 19:54
40

on the assumption that your needs are more like your colored example code then:

.box:nth-child(odd){
    clear:both;
}

if it's going to be 3 rows then nth-child(3n+1)

FatherStorm
  • 7,133
  • 1
  • 21
  • 27
39

I'm providing this answer because even when there are good ones which do provide a solution(using Masonry) still isn't crystal clear why it isn't possible to achieve this by using floats.

(this is important - #1).

A floated element will move as far to the left or right as it can in the position where it was originally

So put it in this way:

We have 2 div

<div class="div5">div5</div>
<div class="div6">div6</div>

.div-blue{
    width:100px;
    height:100px;
    background: blue;
}

.div-red{
    width:50px;
    height:50px;
    background: red;
}

without float they'll be one below the other

enter image description here

If we float: right the div5, the div6 is positioned on the line where the div5 was ,

/*the lines are just for illustrate*/

enter image description here

So if now we float: left the div6 it will move as far to the left as it can, "in this line" (see #1 above), so if div5 changes its line, div6 will follow it.

Now let's add other div into the equation

<div class="div4">div4</div>
<div class="div5">div5</div>
<div class="div6">div6</div>

.div-gree{
    width:150px;
    height:150px;
    background: green;

    float:right;
}

We have this

enter image description here

If we set clear: right to the div5, we are forcing it to take the line bellow div4

enter image description here

and div6 will float in this new line wether to the right or to the left.

Now lets use as example the question that brought me here due to a duplicate Forcing div stack from left to right

Here the snippet to test it:

div{
    width:24%;
    margin-right: 1%;
    float: left;
    margin-top:5px;
    color: #fff;
    font-size: 24px;
    text-align: center;
}

.one{
    background-color:red;
    height: 50px;
}

.two{
    background-color:green;
    height:40px;
}

.three{
    background-color:orange;
    height:55px;
}

.four{
    background-color:magenta;
    height:25px;
}

.five{
    background-color:black;
    height:55px;
}
<div class="one">1</div>
<div class="two">2</div>
<div class="three">3</div>
<div class="four">4</div>
<div class="five">5</div>

<div class="one">1*</div>
<div class="three">2*</div>
<div class="four">3*</div>
<div class="two">4*</div>
<div class="five">5*</div>

enter image description here

In the above image you can see how div.5 is stocked just next to div.3 that is because in its line (defined by the line box of div.4) that is as far it can go, div.1*, div.2*, etc, also float left of div.5 but as they don't fit in that line they go to the line bellow (defined by the line box of div.5)

Now notice that when we reduce the height of div.2* enough to be less than div.4* how it let pass to div.5*:

enter image description here

I hope this helps to clarify why this can not be achieved using floats. I only clarify using floats (not inline-block) because of the title "CSS Floating Divs At Variable Heights" and because right now the answer is quite long.

Community
  • 1
  • 1
Yandy_Viera
  • 4,320
  • 4
  • 21
  • 42
8

As has been rightly pointed out, this is impossible with CSS alone... thankfully, I've now found a solution in http://isotope.metafizzy.co/

It seems to solve the problem fully.

Kyle Hayes
  • 5,225
  • 8
  • 38
  • 53
stevenmc
  • 1,659
  • 4
  • 18
  • 27
  • 23
    I really think you should have given this answer to @thirtydot - he/she suggested using Masonry which is the only way to achieve this. Isotope is the (voluntary) paid for version of Masonry built by the same person. – iamkeir Sep 17 '12 at 17:37
  • It doesn't solve anything. All elements have same width and are positioned absolutely. – Henry Brewer Dec 13 '18 at 16:50
4

With a little help from this comment (CSS Block float left) I figured out the answer.

On every "row" that I make, I add a class name left.
On every other "row" that I make, I add a class name right.

Then I float left and float right for each of these class names!

The only complication is that my content order is reversed on the "right" rows, but that can be resolved using PHP.

Thanks for your help folks!

#holder{
  width:200px;
  border:1px dotted blue;
  display:inline-block;
}
.box{
  width:100px;
  height:150px;
  background-color:#CCC;
  float:left;
  text-align:center;
  font-size:45px;
}
.one{
  background-color:#0F0;
  height:200px;
}

.two{
  background-color:#0FF;
}

.three{
  background-color:#00F;
  float:right;
}

.four{
  background-color:#FF0;
  float:right;
}
.left{float:left;}
.right{float:right;}
<div id="holder">
  <div class="box one left">1</div>
  <div class="box two left">2</div>
  <div class="box four right">4</div>
  <div class="box three right">3</div>
</div>
</body>
Community
  • 1
  • 1
stevenmc
  • 1,659
  • 4
  • 18
  • 27
  • Except that your demo looks nothing like your picture. And it won't work for an "infinite number of divs" as you asked in your question. Try to adapt the code in this answer of yours to use 8 different height `.box` `div`s, for example. – thirtydot Mar 08 '11 at 19:15
  • Also, having `float: left` or `float: right` and `display: inline-block` on the same element makes no sense. `display: inline-block` will be turned into `display: block` (the `div` default) by the `float` rule. In other words, `display: inline-block` is doing precisely nothing. – thirtydot Mar 08 '11 at 19:21
  • Ok, you're right, the display:inline-block isn't right... I've edited my solution appropriately. – stevenmc Mar 14 '11 at 09:21
  • 1
    Hmm. Ok, you're right... I'm still working on a solution. I will post here when complete. Thanks thirtydot. – stevenmc Mar 14 '11 at 09:33
  • Reversing the order of one of your columns is not a good solution. The HTML should know nothing about how it will be styled. – superluminary Nov 30 '12 at 17:16
  • what is height of first div is 100px – Vishal B Sep 21 '17 at 10:59
  • is there any solution for making 1,2,3,4...n in dynamic – Pratik Bhalodiya Jan 04 '18 at 11:01
1

If any one is still looking for alternatives, here's one. Try using the (-moz-/-webkit-) column-width property. It takes care of the variable div height issue. However column-width adds new div at the end of column.

Else, jQuery Masonry works best.

rodiwa
  • 1,690
  • 2
  • 17
  • 35
1

Thanks to thirtydot, I have realised my previous answer did not properly resolve the problem. Here is my second attempt, which utilizes JQuery as a CSS only solution appears impossible:

<html>
    <head>
        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
        <script type="text/javascript" language="javascript">
            $(document).ready(function() {
                var numberOfColumns = 3;
                var numberOfColumnsPlusOne = numberOfColumns+1;
                var marginBottom = 10;  //Top and bottom margins added
                var kids = $('#holder:first-child').children();
                var add;
                $.each(kids, function(key, value) {
                    add = numberOfColumnsPlusOne+key;
                    if (add <= kids.length){
                        $('#holder:first-child :nth-child('+(numberOfColumnsPlusOne+key)+')').offset({ top: $('#holder:first-child :nth-child('+(key+1)+')').offset().top+$('#holder:first-child :nth-child('+(key+1)+')').height()+marginBottom });
                    }
                });
            });             
        </script>
        <style>
            #holder{
                width:270px;
                border:1px dotted blue;
                display:inline-block; /* Enables the holder to hold floated elements (look at dotted blue line with and without */
            }
            .box{
                width:80px;
                height:150px;
                background-color:#CCC;
                margin:5px;
                text-align:center;
                font-size:45px;
            }
            .one{
                height:86px;
            }
            .two{
                height:130px;
            }
            .three{
                height:60px;
            }
            .four{
                clear:both;
                height:107px;
            }
            .five{
                height:89px;
            }
            .six{
                height:89px;
            }
            .left{float:left;}
            .right{float:right;}
        </style>
    </head>
    <body>      
        <div id="holder">
            <div class="box one left">1</div>
            <div class="box two left">2</div>
            <div class="box three left">3</div>
            <div class="box four left">4</div>
            <div class="box five left">5</div>
            <div class="box six left">6</div>
        </div>
    </body>
</body>

The only problem that remains for my solution is, what happens when a box is two-box-widths instead of just one. I'm still working on this solution. I'll post when complete.

stevenmc
  • 1,659
  • 4
  • 18
  • 27
  • 4
    *"Here is my second attempt"*..? WHAT? Why are you posting all your attempts as different answers..? You can simply [edit] your answer and update it the way you want. – T J Oct 18 '14 at 16:54
0

This may not be the exact solution for everybody but I find that (quite literally) thinking outside the box works for many cases: in stead of displaying the the boxes from left to right, in many cases you can fill the left column first, than go to the middle, fill that with boxes and finally fill the right column with boxes. Your image would then be:

order of filling :

If you are using a scripting language like php you can also fill the columns from left to right by adding a new box to it and outputting when all columns are filled. eg (untested php code):

$col1 = '<div class="col1"> <div>box1</div>';
$col2 = '<div class="col2"> <div>box2</div>';
$col3 = '<div class="col3"> <div>box3</div>';
$col1 .= '<div>box4</div> </div>'; //last </div> closes the col1 div
$col2 .= '<div>box5</div> </div>';
$col3 .= '<div>box6</div> </div>';
echo $col1.$col2.$col3;

$col1, $col2 and $col3 can have float:left and width: 33%, set the boxes inside the div to full width and no float.

Obviously if you are using javascript / jquery to load the boxes dynamically you are better of styling them this way as well, as explained in other answers to this thread.

Community
  • 1
  • 1
bolvo
  • 361
  • 3
  • 13
-1

to display just put this css to your div and then you have the desired layout. no need of any plug in or JS.

 .Your_Outer {
    background-color: #FFF;
    border: 1px solid #aaaaaa;
    float: none;
    display:inline-block;
    vertical-align:top;
    margin-left: 5px;
    margin-bottom: 5px;
    min-width: 152.5px;
    max-width: 152.5px;
}

you can edit the code as per your requirements :)

Shahid Amin
  • 41
  • 11
-3

On modern browsers you can simply do:

display: inline-block;
vertical-align: top;
Flavio Tordini
  • 535
  • 4
  • 11
  • Why was this voted down? – superluminary Nov 30 '12 at 17:17
  • Please see this http://stackoverflow.com/a/5234858/428302 – vinay Dec 05 '12 at 13:19
  • Well, it does work and the snippet you linked to does work too (at least in Chrome). So you might want to give us a better explanation. – Flavio Tordini Jan 29 '13 at 10:26
  • The previous question links the following fiddle which shows how this method turns out: http://jsfiddle.net/bCgea/1/ It's not bad, but not what they are looking for either. The column flow is not continuous. – Eric G Jan 31 '13 at 20:49