5

I'm using flex. There seems to be an unexpected margin difference between two div elements - the third row has a different margin between the divs than the first two rows:

enter image description here

How do I amend the code so that the third row's margin is the same as the first two rows?

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

.container:after {
  flex: 1;
  content: '';
}

.container form {
  width: 100%;
  display: flex;
}

.container .comment {
  flex: 1;
}

.square {
  padding: 10px;
  width: calc(100% / 9);
  margin: 0.7vw 0 0.7% 1vw;
  background: red;
}
<div class="container">
  <form method="post" style="margin: 0.7vw 0 0.7% 1vw;">
    <input class="comment" type="text">
    <input type="submit">
  </form>

  <div class="square">
  </div>
  <div class="square">
  </div>
  <div class="square">
  </div>
  <div class="square">
  </div>
  <div class="square">
  </div>
  <div class="square">
  </div>
  <div class="square">
  </div>
  <div class="square">
  </div>
  <div class="square">
  </div>
  <div class="square">
  </div>
  <div class="square">
  </div>
  <div class="square">
  </div>
  <div class="square">
  </div>
  <div class="square">
  </div>
</div>

JsFiddle: https://jsfiddle.net/4ydyoqmx/

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
The Codesee
  • 3,714
  • 5
  • 38
  • 78

5 Answers5

4

The problem is that your pseudo-element is consuming all available space on the last row.

enter image description here

Yes, this is done to neutralize the effect of justify-content: space-between on two flex items, which causes them to appear on opposite ends...

enter image description here

... but it also kills the proportional spacing that space-between provides when the items fill the row.

Until the flex specification is revised with specific last-row alignment properties, you could implement a hack that solves this problem:

Add invisible flex items after the last real item.

In the example below, I've added seven phantom items to your code.

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

/*
.container:after {
  flex: 1;
  content: '';
}
*/

.invisible { visibility: hidden; }

.container form {
  width: 100%;
  display: flex;
}

.container .comment {
  flex: 1;
}

.square {
  padding: 10px;
  width: calc(100% / 9);
  margin: 0.7vw 0 0.7% 1vw;
  background: red;
}
<div class="container">
  <form method="post" style="margin: 0.7vw 0 0.7% 1vw;">
    <input class="comment" type="text">
    <input type="submit">
  </form>

  <div class="square"></div>
  <div class="square"></div>
  <div class="square"></div>
  <div class="square"></div>
  <div class="square"></div>
  <div class="square"></div>
  <div class="square"></div>
  <div class="square"></div>
  <div class="square"></div>
  <div class="square"></div>
  <div class="square"></div>
  <div class="square"></div>
  <div class="square"></div>
  <div class="square"></div>
  
  <div class="square invisible"></div>
  <div class="square invisible"></div>
  <div class="square invisible"></div>
  <div class="square invisible"></div>
  <div class="square invisible"></div>
  <div class="square invisible"></div>
  <div class="square invisible"></div>
</div>

Revised Fiddle

More information:


Sidenote

There's a good chance you'll encounter some trouble with this rule:

.square { margin: 0.7vw 0 0.7% 1vw; }

The flexbox specification advises against using percentage margin and padding in a flex container.

Authors should avoid using percentages in paddings or margins on flex items entirely, as they will get different behavior in different browsers.

Here's some more:

4.2. Flex Item Margins and Paddings

Percentage margins and paddings on flex items can be resolved against either:

  • their own axis (left/right percentages resolve against width, top/bottom resolve against height), or,
  • the inline axis (left/right/top/bottom percentages all resolve against width)

A User Agent must choose one of these two behaviors.

Note: This variance sucks, but it accurately captures the current state of the world (no consensus among implementations, and no consensus within the CSSWG). It is the CSSWG’s intention that browsers will converge on one of the behaviors, at which time the spec will be amended.

Community
  • 1
  • 1
Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
  • 1
    Thank you for this brilliant answer _(it's quite amazing actually!)_. While I hadn't thought of adding invisible divs to make the last row mirror the first two rows, I don't believe it would be the most convenient solution as I am later planning on using my code in a `foreach` loop and I feel it would be a little complicated having to use a PHP function to count the number of divs and depending on how many there are, substitute hidden ones. I've upvoted your answer as I feel it'll be brilliant for other users who encounter the same problem :) – The Codesee May 23 '16 at 19:29
  • 1
    Hi Michael, its been over a year now and I was wondering if the only option is still to implement a 'hack' or whether the specification has been revised? – The Codesee Dec 27 '17 at 21:06
  • Hi. Nothing has yet changed with flex. The last-row alignment problem still exists. However, with the advent of CSS Grid, there's now a clean, simple and easy solution: https://jsfiddle.net/4ydyoqmx/12/ – Michael Benjamin Dec 27 '17 at 21:27
  • More details: [Targeting flex items on the last row](https://stackoverflow.com/q/42176419/3597276) – Michael Benjamin Dec 27 '17 at 21:29
  • Grid demo explained: [Equal width flex items even after they wrap](https://stackoverflow.com/q/44154580/3597276) – Michael Benjamin Dec 27 '17 at 21:29
  • Oh, that seems cool - I'll have a look at it! Is it possible to set the max number of items per row? – The Codesee Dec 27 '17 at 22:52
  • Yes, you can set a maximum number of items per row. In the revised demo below, I've set the max to 5. Their width is an equal distribution of the space on the row. https://jsfiddle.net/4ydyoqmx/13/ – Michael Benjamin Dec 27 '17 at 23:00
2

There is a few different ways of doing this, depending on how you want the "squares" to respond:

flex-grow:

.square {
    flex-grow: 1;
    padding: 10px;
    width: calc(100% / 9);
    margin: 0.7vw 0 0.7% 1vw;
    background: red;
}

Fiddle: https://jsfiddle.net/4ydyoqmx/2/

inline-block:

.square {
    display: inline-block;
    padding: 10px;
    width: calc(100% / 9);
    margin: 0.7vw 0 0.7% 1vw;
    background: red;
}

Fiddle: https://jsfiddle.net/4ydyoqmx/4/

Third Option:

Removed content: '' from .container:after because that was causing problems

theblindprophet
  • 7,767
  • 5
  • 37
  • 55
0

It's the justify-content: space-between setting on .container which does this (in conjunction with flex-wrap: wrap;): It evently distributes the elements in those lines that are filled, but in the last line that would not look good, so it leaves the predefined / default margin between those elements. (That's similar to a justified text paragraph)

One possibility to avoid this is to simply erase justify-content: space-between, thereby setting it to the defaultleftalignment (but they won't be distributed across the whole width anymore):

https://jsfiddle.net/h8ejaob7/

Johannes
  • 64,305
  • 18
  • 73
  • 130
  • I was trying to avoid this as I wanted the last div to end where the submit button ends _(if you get what I mean)_ – The Codesee May 23 '16 at 17:07
  • I really don't see a reason why this is downvoted (explanation?). But it seems a big downvoter has been riding along all these answers... – Johannes May 23 '16 at 19:09
  • I have upvoted your answer as I believe it contains valuable information and doesn't deserve a downvote :) – The Codesee May 23 '16 at 19:14
0

It is doing like that because you're justifying content using space between. So on the screen it can not accommodate 7th div so it puts only 6 div on screen and puts equal space between 6 div elements.But in the last line there has only 2 div's so that means your 3rd line is not fully filled with div elements. That's why it is not putting space as it did on upper line's div's .

If you put 4 more div's then you'll get exact effect as it has on upper line's .

Abhishek Panjabi
  • 439
  • 4
  • 23
-2

This problem could be solved by adding this piece code to your css..

*{margin:0px;
padding:0px}

Hope it works..

  • @SangameshDavey it didn't fix the problem, [see here](https://jsfiddle.net/7a0xyhhk/). Make sure to resize so there are six items in the first two rows. – hungerstar May 23 '16 at 17:17