0

I have a parent div containing an unknown number of smaller divs that are used like large icon-like buttons. If a row of child divs is full, I would like them to have equal margins on each side (ie. centered), but if a row is not full I would like them to be filled in from the left side (but still in columns with the elements above). Is there a way to do this with CSS? Resizing the window should maintain the centering and add/remove columns as necessary. All the child div widths are known.

Here's a crappy image of the behavior I'm trying for: image

unbootabru
  • 347
  • 1
  • 3
  • 9
  • 1
    Search for `flex` css... https://css-tricks.com/snippets/css/a-guide-to-flexbox/ ... and then about `margin`, `padding`, .... – nelek Aug 05 '15 at 20:14
  • @Paulie_D Normally I would agree, but this question seems really self-explanatory to me (especially with image), and since it's an overall layout question, it makes sense that op doesn't have any code to show. – Maximillian Laumeister Aug 05 '15 at 20:18
  • Take a look at [Bootstrap grid system](http://www.w3schools.com/bootstrap/bootstrap_grid_system.asp) or [Foundation grid system](http://foundation.zurb.com/grid.html), that may help. – Zakaria Acharki Aug 05 '15 at 20:19
  • 1
    Basically, not even flexbox (AFAIK) can do that...it can get close but the side margins will not be the same. Love to be proved wrong. - http://codepen.io/Paulie-D/pen/oXmGKQ Note this is the same basic layout issue with `inline-block` and `float`. None of them can do that either. – Paulie_D Aug 05 '15 at 20:29
  • The only method I can think of is `text-align-last` but that has very poor support and may, in fact be CSS Level 4 – Paulie_D Aug 05 '15 at 20:44
  • @Michael_B Nope...the number of columsn is unknown and depends on the width of the viewport. – Paulie_D Aug 05 '15 at 20:45
  • if you look at the two images, one has more columns that the other. – Paulie_D Aug 05 '15 at 20:47
  • number of columns is solely dependent on window size (not a fixed number) – unbootabru Aug 05 '15 at 20:49
  • @ Paulie_D - text-align-last requires invisible elements as well - https://jsfiddle.net/ap6eg3xp/2/ . Support is surprisingly good - ie5.5?!, firefox with -moz- and chrome. – Ori Drori Aug 05 '15 at 21:01
  • Chrome requires an experimental flag (and still has bugs I believe) and Safari is a complete no-no. Don't know anything about invisibe elements being *required* should just work without them in the spec (AFAIK) - https://developer.mozilla.org/en-US/docs/Web/CSS/text-align-last – Paulie_D Aug 05 '15 at 21:42
  • This may help you or others: http://stackoverflow.com/a/32811002/3597276 – Michael Benjamin Oct 23 '15 at 15:54

3 Answers3

0

Just use float left, and items of the width (the original fiddle):

<div class="container">
    <div class="item">Cats</div>
    <div class="item">Cats</div>
    <div class="item">Cats</div>
    <div class="item">Cats</div>
    <div class="item">Cats</div>
    <div class="item">Cats</div>
    <div class="item">Cats</div>
    <div class="item">Cats</div>
    <div class="item">Cats</div>
    <div class="item">Cats</div>
    <div class="item">Cats</div>
    <div class="item">Cats</div>
    <div class="item">Cats</div>
</div>

.container {
    overflow: auto;
}

.item {
    width: 50px;
    float: left;
    margin: 0 10px;
}
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
  • 1
    This does not answer OP's question. The margins are not the same on both sides of the containing element. – Maximillian Laumeister Aug 05 '15 at 20:25
  • Yep - you're right. Missed that. The right size image was a bit misleading. – Ori Drori Aug 05 '15 at 20:28
  • 1
    Even with the updated flexbox method the flow doesn't work as the OP requested. This just isn't possible (AFAIK) with any flowing layout method. – Paulie_D Aug 05 '15 at 20:34
  • I think that we understand the OP differently. The code above matches the images - same margin on right and left of container, elements are array in columns, and have the margin between them. – Ori Drori Aug 05 '15 at 20:38
  • if you look at the second image (the narrow one) he doesn't want the last row centered or spaced around, he wants them aligned left. – Paulie_D Aug 05 '15 at 20:41
  • The bottom row doesn't match/work correctly. I have a feeling it's unpossible, but thought I'd ask anyways. – unbootabru Aug 05 '15 at 20:43
  • Now I see that @ Paulie_D. Then the closest answer is the float:left, although the margins on both sides are not equal. – Ori Drori Aug 05 '15 at 20:45
0

Okay I figured out a solution for this that both allows for equal margins and ALSO aligns divs left in the last row. The caveat is that it uses hidden elements, so the container is taller than the visible elements it contains.

Also it adds a bunch of extra markup to your code. If I thought of any other way to do this, I would do it differently. Sorry.

JSFiddle: https://jsfiddle.net/88qadmo3/2/

#container > div {
    margin: 10px;
    width: 100px;
    height: 100px;
}
.floatleft {
    background-color: red;
}

.invis {
    visibility: hidden;
}

#container {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    background-color: #DDDDDD;
}

.clearfix {
    clear: both;
}
<div id="outer">
<div id="container">
    <div class="floatleft"></div>
    <div class="floatleft"></div>
    <div class="floatleft"></div>
    <div class="floatleft"></div>
    <div class="floatleft"></div>
    <div class="floatleft"></div>
    <div class="floatleft"></div>
    <div class="floatleft"></div>
    <div class="floatleft"></div>
    <div class="floatleft"></div>
    <div class="floatleft"></div>
    <div class="floatleft"></div>
    <div class="floatleft"></div>
    <div class="floatleft"></div>
    
    <div class="invis"></div>
    <div class="invis"></div>
    <div class="invis"></div>
    <div class="invis"></div>
    <div class="invis"></div>
    <div class="invis"></div>
    <div class="invis"></div>
    <div class="invis"></div>
    <div class="invis"></div>
    <div class="invis"></div>
    <div class="invis"></div>
    <div class="invis"></div>
    <div class="invis"></div>
    <div class="invis"></div>
</div>
</div>
Maximillian Laumeister
  • 19,884
  • 8
  • 59
  • 78
  • 1
    I'd thought of that but, *shudders*, it just feels dirty. – Paulie_D Aug 05 '15 at 20:46
  • 2
    I tried using pseudo-elements but *actual* empty elements just for layout...I can't support that. – Paulie_D Aug 05 '15 at 20:49
  • @Paulie_D My apologies, I know this is really bad code, but I still think it gets as close to what OP wanted as possible. – Maximillian Laumeister Aug 05 '15 at 20:52
  • @Paulie_D Instead of putting empty elements in the source, you could always dynamically create them using jQuery (/s) – Maximillian Laumeister Aug 05 '15 at 20:56
  • If it's dirty and its the _only_ solution is it really that dirty? If I had to use this, I would need to add an equal number of invis divs to parent, or just enough to guarantee filling the row? Also not 100% on the vertical issue?; The container div has a fixed height (-ish, it goes to bottom of window + margin). That could cause scrolling issues yeah? – unbootabru Aug 05 '15 at 20:57
  • If we were using JS we wouldn't need flexbox >). Secondly, if you went the invisible div route you can give them zero height and adjust the margins http://codepen.io/Paulie-D/pen/xGMPRQ – Paulie_D Aug 05 '15 at 20:58
  • @user3784402 To guarantee filling the row, you would need `n-1` invis divs for `n` content divs, which satisfies the case where there are two rows, and the second row has one content div. Also, if you disable scrolling on an outer container div with `overflow: hidden;` then you wouldn't have any scrolling issues, but that only works for specific use cases (possibly in yours, not sure). – Maximillian Laumeister Aug 05 '15 at 21:03
0

You can do this by wrapping each of the child elements in an element and using CSS media queries to change the size of the wrappers. Then, just use % width on the wrappers based on what you want the column sizes to be.

When you run the example, shrink it to <400px so you can see it move from 6 columns to 3 (you'll need to run the code snippet on a full-page to see this).

.parent {
  border: 2px solid red;
}
.child {
  width: 100px;
  display: block;
  margin: 0 auto;
  border: 1px solid black;
}
.wrapper {
  display: inline-block;
  text-align: center;
  float: left;
  width: 16.66%;
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
.clear {
  clear: both;
}
@media (max-width: 800px) {
  .wrapper {
    width: 20%;
  }
}
@media (max-width: 600px) {
  .wrapper {
    width: 25%;
  }
}
@media (max-width: 400px) {
  .wrapper {
    width: 33.33%;
  }
}
<div class="parent">
  <div class="wrapper">
    <div class="child">1</div>
  </div>
  <div class="wrapper">
    <div class="child">2</div>
  </div>
  <div class="wrapper">
    <div class="child">3</div>
  </div>
  <div class="wrapper">
    <div class="child">4</div>
  </div>
  <div class="wrapper">
    <div class="child">5</div>
  </div>
  <div class="wrapper">
    <div class="child">6</div>
  </div>
  <div class="clear"></div>
</div>
jperezov
  • 3,041
  • 1
  • 20
  • 36