How to set auto-margin boxes in flexible-width design using CSS?
Asked Answered
G

8

12

I have DIV with flexible width set e.g. min-width:800px and max-width:1400px. In this DIV, there are many boxes with fix width 200px and display:inline-block. So depending on parent DIV width, these boxes fill the entire space.

My problem is the blank space on the right side which is caused by variable width of the parent div. Sometimes this blank space is small and looks fine, but with different widths of the parent div, this blank space is almost 200px.

I don't know, if I described my problem in enough detail, I hope this picture will help to describe my actual situation:

enter image description here

And this is what I would like to have:

enter image description here

This auto-margin could be easily achieved by using TABLE. However, I don't know the exact number of columns, since it depends on user's screen resolution. So I can't use table and rather stick with CSS.

Anyone has an idea how to solve this ? Thank you in advance for your comments and answers.

EDIT: I don't need support of IE6. I would like to support IE7, but IE7 is optional as I know there are limitations so I will probably use fixed width of "div.wrapper" in IE7

EDIT2 I need to handle multiple rows of these boxes, so they don't exceed the "div.wrapper" box and wrap correctly in multiple lines of boxes, not just in one long line.

EDIT3 I don't know the number of "columns" as this is very variable depending on user's screen resolution. So on big screen there could be 7 boxes in one row, and on small screens there could be just 4 boxes in one row. So I need solution that doesn't set fixed number of boxes in one row. Instead, when the boxes don't fit in one row, they should just wrap to a next row.

Gq answered 12/8, 2011 at 14:53 Comment(4)
The golden question: which browsers/versions do you need to support?Lib
thirtydot: thanks for asking, I've updated my questionGq
Not sure on an easy CSS solution (without converting the box width to percentage), you could just use another wrapper though, to ensure the boxes are always centered.Ancy
I'm afraid CSS3 tag is irrelevant for this question.Michaud
L
5

This is as close as IE7-compatible CSS can get: http://jsfiddle.net/thirtydot/79mFr/

If this still isn't right, it's time to look at using JavaScript and hopefully also jQuery. If you define your requirements properly, it should be trivial to get this perfect with JavaScript.

HTML:

<div id="container">
    <div></div>
    <div></div>
    ..
    <span class="stretch"></span>
</div>

CSS:

#container {
    border: 2px dashed #444;

    text-align: justify;
    -ms-text-justify: distribute-all-lines;
    text-justify: distribute-all-lines;

    min-width: 800px;
    max-width: 1400px
}

#container > div {
    margin-top: 16px;
    border: 1px dashed #f0f;
    width: 200px;
    height: 200px;
    vertical-align: top;
    display: inline-block;
    *display: inline;
    zoom: 1
}
.stretch {
    width: 100%;
    display: inline-block;
    font-size: 0;
    line-height: 0
}

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/79mFr/2/

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></div>
    <div></div>
</div>

And this does:

<div id="container">
    <div></div>
    <div></div></div>
Lib answered 12/8, 2011 at 15:36 Comment(8)
#container > div will break. Legacy IE you need to use #container>div (no spaces).Mullein
@AlienWebguy: Nobody cares about IE 5.Lib
+1 nice solution. I had a go with a CSS3 box-flex solution but had issues with margins :-/Dodecasyllable
@andyb: I was thinking of trying that myself when I initially read the question (especially because of the css3 tag). But then I asked about the browser support, and the answer was: I would like to support IE7, but IE7 is optional - meaning IE8 support is required. And that was the end of that :)Lib
@Lib I'm not talking about IE5. IE6 & IE7 can't always interpret the space between parent > child properly, hence why parent>child will work as an alternative. This is a known bug. Apparently not that widely known, eh?Mullein
@AlienWebguy: Can you provide a citation for what you're saying?Lib
I am confused as to why the 2nd row of boxes acts retarded. If there are 2 boxes in the 2nd row, and 6 on the top, the first box in the 2nd row will be under box 1 and the 2nd box in the 2nd row will be under box 6.Mackenzie
@Matt Anderson: That's just how text-align: justify works. Maybe it's clearer with actual text: jsfiddle.net/tXXsn. There is nothing closer using only CSS that even comes close to fitting the OP's requirements that also works in IE8. So I'm working with what there is, which is text-align: justify.Lib
M
1

You can float them and just apply a wrapper to the .box which will allow you to margin:auto; the .box relative to the floated wrapper.

CSS:

div.wrapper {
    width:100%;
    border:3px solid red;
}
div.clear {
    clear:both;
}
div.box-wrapper {
    float:left;
    margin:10px 0;
    height:100px;
    width:20%;
}
div.box {
    border:1px solid black;
    width:80px;
    height:100px;
    margin:auto;
}

HTML:

<div class="wrapper">
    <div class="box-wrapper"><div class="box"></div></div>
    <div class="box-wrapper"><div class="box"></div></div>
    <div class="box-wrapper"><div class="box"></div></div>
    <div class="box-wrapper"><div class="box"></div></div>
    <div class="box-wrapper"><div class="box"></div></div>
    <div class="box-wrapper"><div class="box"></div></div>
    <div class="box-wrapper"><div class="box"></div></div>
    <div class="box-wrapper"><div class="box"></div></div>
    <div class="box-wrapper"><div class="box"></div></div>
    <div class="box-wrapper"><div class="box"></div></div>
    <div class="clear"></div>
</div>

Demo: http://jsfiddle.net/2avwf/

I didn't make them 200px wide for the sake of the fiddle window. Just swap that width:80px out with the width you desire.

If you want to make this a dynamic solution, in which the number of boxes in a row will vary from user to user based off their screen size, etc., simply make 3 or 4 width-defining box-wrapper classes:

.box-wrapper-25 {
    width:25%;
}
.box-wrapper-33 {
    width:33%;
}

Then with JQuery you can easily detect the width of .wrapper and assign an override class to the box wrappers:

$('.box-wrapper').each(function(){
    $(this).removeClass().addClass('box-wrapper box-wrapper-25'); // only need 4 per row
});

Something like this: http://jsfiddle.net/RcDky/

Mullein answered 12/8, 2011 at 15:14 Comment(1)
Your solutions looks good, but it has a flaw that it sets a fixed number of boxes in row. And when I have large screen the margins of the boxes are larger than boxes themselves. Could you please update your answer so it doesn't set fixed number of boxes in one row ?Gq
M
1

You need to make .box inline-blocks, and justify text in .wrapper. .wraper:after is needed to justify the last line. Older IEs don't understand after, but in IE text-align-last:center will take care of the last line.

.wrapper{
    text-align:justify;
    max-width:1400px;
    min-width:800px;
    text-align-last:center;
}
.wrapper:after{
    content:'';
    display:inline-block;
    width:100%;
    height:0;
    font-size:0;
    line-height:0;
}
.box{
    display:inline-block;
    *display:inline;
    vertical-align:top;
    width:200px;
    height:50px;
    background:red;
}

Here's a jsfiddle.

Michaud answered 12/8, 2011 at 15:33 Comment(4)
+1. Who would -1 a thorough and fundamented answer without explaining? This works as intended as far as I can tell.Swadeshi
-1 Did you two even look at the fiddle? It is completely broken in both chrome and firefox.Mackenzie
+1. I didn't notice your answer before, it's similar to my (later) answer, what with our mutual use of text-align:justify. I'm going to borrow the use of :after for my own answer.Lib
@Matt Anderson Maybe you should upgrade from FF2, It looks the same in Opera, Chrome and Firefox.Michaud
C
0

Try this jsFiddle: http://jsfiddle.net/MKuxm/

Just make the window larger and smaller to size the div, you'll see that the margin between the red boxes will size accordingly. I am aware that the red boxes are no longer 200px wide, but I'm afraid that isn't possible with pure css because you should not mix percentage widths and fixed pixel width.

HTML

<div>
     <span>TEXT</span>
     <span>TEXT</span> 
     <span>TEXT</span> 
     <span>TEXT</span> 
</div>

CSS

div {
    width: 95%;
}

span {
    float: left;
    background: red;
    width: 20%;
    margin-left: 2.5%;
    margin-right: 2.5%;
}
Caliban answered 12/8, 2011 at 15:2 Comment(1)
Thanks for answer, however I really need those boxes to be fixed width of 200px and also I need to handle wrapping to multiple rows if all the boxes don't fit in one row.Gq
C
0

I answered a similar question here

This is possible in pure css3 using media queries and the css calc() routine.

Of coarse this will only work on modern browsers. IE9+,Chrome,Firefox,

See this WORKING DEMO

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).

Crozier answered 30/4, 2013 at 22:53 Comment(0)
S
0

On my project I have faced with the same problem and I came to the next decision - the best way for me is to go with js, in my case you can have xxx count of block inside container, if there is enough space in 1st row the block from 2nd row goes up to the 1st row, and so on. here is an example http://jsfiddle.net/gVAjN/11/

 $(function() {
  // Call function when DOM is ready
  settingsAlignment();

$(window).resize(function() {
      // Call function on window resize
        settingsAlignment();
    })

$('#new_div').click(function() {
    box_number = $('.element').size();
    box_add = box_number + 1;
    $('.container').append($('<div class="element">Box'+ box_add + '</div>'))
    settingsAlignment();
})

function settingsAlignment() {
// calculation of ul's padding-left and li's margin-right
var settingsUl = $('.container');
    settingsLi = $('.element');
    ul_width = settingsUl.outerWidth(true);
    item_width = settingsLi.width();
    min_gap = 7;
    effective_item_width = item_width + min_gap;
    items_in_row = Math.floor((ul_width - min_gap) / effective_item_width);
    gaps_sum = ul_width - items_in_row * item_width;
    new_gaps = gaps_sum / (items_in_row + 1);
    item_margin = Math.floor(new_gaps);
    row_width = (item_width + item_margin) * items_in_row - item_margin;
    console.log(row_width + '= row_width');
    console.log(ul_width + '= ul_width');
    ul_left_padding = Math.ceil((ul_width - row_width) / 2);
    console.log(ul_left_padding + '=ul_left_padding');
    settingsUl.css('padding-left', ul_left_padding + 'px');
    settingsLi.css('margin-right', item_margin + 'px');
  console.log(settingsLi);
 }
});
Stationery answered 18/5, 2013 at 8:9 Comment(0)
D
0

quite old but worth trying since multiple rows and text-align: justify; in the #container creates gaps when last row has less divs. I wanted everything to be floated left. So my idea was to use 2 wrappers.

<div class="wrapper">
    <div class="wrapper2">    
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
        <div class="clear"></div>
    </div>
</div>

as well as overflow: hidden; in css

.wrapper {
    width:620px;
    border:3px solid red;
    margin:0 auto; overflow:hidden;
}
.wrapper2 {
    width:630px;
}
div.clear {
    clear:both;
}
.box {
    width:200px; background:#000; height:100px; margin-bottom:10px; float:left; overflow:hidden; margin-right:10px;
}

drawback: margins are not auto set...

DEMO: http://jsfiddle.net/hexagon13/2avwf/52/

Disused answered 9/12, 2014 at 3:57 Comment(0)
P
0

Try this:

div.wrapper {
    display: flex;
    align-items: stretch;
    width: 100%;
    height: 100%;
    justify-content: space-between;
    /* justify-content will give the auto margin you looking for
       it will place the auto margin only between each div.box
       make sure the div.wrapper has "display: flex;"
     */
}

div.box {
    display: inline-flex; /* inline-flex will make the boxes all in the same line */
    width: 200px; /* notice you don't need width to be a % for this to work */
    height: 100%;
    margin: auto; /* gives you the auto margin for the first and last box from the border of your wrapper */
}
Pannell answered 2/3, 2017 at 1:33 Comment(1)
sorry its my first time using this site and it gave me a hard time to post and I didn't get how to format it to avoid the error it gave me when I tried to post that's why it has lots of "enter code here" it looks a mess but if you still looking for an answer just email me or send me your email ill send you the code. I was struggling with the same problem and it worked for me. my email is [email protected]Pannell

© 2022 - 2024 — McMap. All rights reserved.