0

I have a container with a dynamic number of items.

Each line can contain up to 4 items. If there are more than 4 items, the next item will start a new line (image 1). If there are less than 4 items it's OK, they just won't fill the whole line (image 2).

But I'm having troubles with the spaces between them:

I tried to use margin-right but it affects the last items in the lines (e.g.: item #4).

I tried to use justify-content: space-between but it looks good only for 4 items and up. For 3 and bellow, it creates a big space between them and I want them to look as in image 2.

Any other elegant / easy solutions?

enter image description here

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

.item {
  display: inline-block;
  width: calc(25% - 12px);
  /* margin-right: 12px; */
}
<div class="container">
  <div class="item">
    #1
  </div>
  <div class="item">
    #2
  </div>
  <div class="item">
    #3
  </div>
  <div class="item">
    #4
  </div>
</div>
TamarG
  • 3,522
  • 12
  • 44
  • 74
  • @Paulie_D: I'm not sure that this question should be closed as a duplicate of that one since I feel the answers here are more useful. I'm not voting to reopen due to potential bias, since I've posted an answer. Also there may be a 'better' duplicate elsewhere. – David Thomas Mar 20 '21 at 21:15

3 Answers3

1

You can use css grid, you have to use display: grid;, use grid-template-columns to set the amount of columns that you want (1fr = 1 parent container fraction) and finally use grid-gap to set the space between your items.

.container {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
    grid-gap: 12px;
}
.item {
    display: inline-block;
    border: 1px solid red;
    color: red;
}
<div class="container">
   <div class="item">
       #1
   </div>
   <div class="item">
       #2
   </div>
   <div class="item">
       #3
   </div>
   <div class="item">
       #4
   </div>
</div>

.container {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
    grid-gap: 12px;
}
.item {
    display: inline-block;
    border: 1px solid red;
    color: red;
}
<div class="container">
   <div class="item">
       #1
   </div>
   <div class="item">
       #2
   </div>
   <div class="item">
       #3
   </div>
   <div class="item">
       #4
   </div>
   <div class="item">
       #5
   </div>
   <div class="item">
       #6
   </div>
</div>

More info about Css grid Here!

0

In class .item, is defined width with calc(25% - 12px). Remember, 25% is just 4 items in each line. 20% is 5 items in each line.

So, change the width to calc(20% - 12px)

Lucas Módolo
  • 177
  • 2
  • 10
0

While CSS Grid is possibly the better solution for the problem, it's entirely possible to solve the problem with CSS flex-box layout, using the gap property and taking advantage – as did your original code – of the calc() function:

// this is to allow you to dynamically add more .item elements
// so you see that it should meet your needs containing more
// elements.
// we use document.querySelector() to retrieve the first element
// that matches the selector (if any exist, otherwise null):
const button = document.querySelector('button'),
  // defining a named function to handle addition of new .item
  // elements:
  addMore = () => {
    // finding the first .item element on the page:
    let base = document.querySelector('.item');
    // finding the .container element, and using
    // .append() to attach a cloned copy of the first
    // .item:
    document.querySelector('.container').append(base.cloneNode(true));
  }
// binding the named - addMore() - function as the event-handler
// for the 'click' event:
button.addEventListener('click', addMore);
*,
 ::before,
 ::after {
  /* selecting all elements, and the pseudo-elements ::before
     and ::after, setting their box-sizing model to border-box
     in order that their widths include their border and padding
  */
  box-sizing: border-box;
  /* removing margin and padding: */
  margin: 0;
  padding: 0;
}

.container {
  display: flex;
  flex-wrap: wrap;
  /* using the gap property to place a 0.5em 'gutter'
     between adjacent elements, both horizontally and
     vertically: */
  gap: 0.5em;
}

.item {
  /* setting the flex-grow: to 1, flex-shrink to 0,
     and flex-basis to the result of 20% of the parent-
     width minus 0.5em (the gap-space): */
  flex: 1 0 calc(20% - 0.5em);
}


/* irrelevant, purely for aesthetics */

*,
 ::before,
 ::after {
  line-height: 2em;
}

.container {
  border: 1px solid #000;
  counter-reset: itemCount;
  width: 90vw;
  margin-inline: auto;
}

.item {
  background-color: lightblue;
  flex: 1 0 calc(20% - 0.5em);
}

.item:nth-child(odd) {
  background-color: palegreen;
}

.item::before {
  content: counter(itemCount, decimal-leading-zero);
  counter-increment: itemCount;
}
<button type="button">Add another item</button>
<div class="container">
  <div class="item">
  </div>
  <div class="item">
  </div>
  <div class="item">
  </div>
  <div class="item">
  </div>
</div>

JS Fiddle demo.

References:

David Thomas
  • 249,100
  • 51
  • 377
  • 410