137

What I wanna do is to make a CSS grid with a dynamic number of cells. For the sake of simplicity, let's assume there will always be four cells per row. Can I specify a grid with such a dynamic number of rows?

To make it easier, here's the Flexbox implementation:

const COLORS = [
  '#FE9',
  '#9AF',
  '#F9A',
  "#AFA",
  "#FA7"
];

function addItem(container, template) {
  let color = COLORS[_.random(COLORS.length - 1)];
  let num = _.random(10000);
  
  container.append(Mustache.render(template, { color, num }));
}

$(() => {
  const tmpl = $('#item_template').html()
  const container = $('#app');
  
  for(let i=0; i<5; i++) { addItem(container, tmpl); }
  
  $('#add_el').click(() => {
    addItem(container, tmpl);
  })
  
  container.on('click', '.del_el', (e) => {
    $(e.target).closest('.item').remove();
  });
});
.container {
  width: 100%;
  display: flex;
  flex-flow: row wrap;
  justify-content: flex-start;
}
.container .item {
  flex: 0 0 calc(25% - 1em);
  min-height: 120px;
  margin: 0.25em 0.5em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/2.3.0/mustache.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="app" class="container">
</div>

<button id="add_el">Add element</button>

<template id="item_template">
  <div class="item" style="background: {{color}}">
    <p>{{ num }}</p>
    <p>
      <button class="del_el">Delete</button>
    </p>
  </div>
</template>

P.S. Apparently, I wasn't clear enough the first time... I want to recreate this effect using the latest CSS Grid Layout.

art-solopov
  • 4,289
  • 3
  • 25
  • 44

7 Answers7

94

Okay, after reading the MDN reference, I found the answer! The key to dynamic rows (or columns) is the repeat property.

const COLORS = [
  '#FE9',
  '#9AF',
  '#F9A',
  "#AFA",
  "#FA7"
];

function addItem(container, template) {
  let color = COLORS[_.random(COLORS.length - 1)];
  let num = _.random(10000);
  
  container.append(Mustache.render(template, { color, num }));
}

$(() => {
  const tmpl = $('#item_template').html()
  const container = $('#app');
  
  for(let i=0; i<5; i++) { addItem(container, tmpl); }
  
  $('#add_el').click(() => {
    addItem(container, tmpl);
  })
  
  container.on('click', '.del_el', (e) => {
    $(e.target).closest('.item').remove();
  });
});
.container {
  width: 100%;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: repeat(auto-fill, 120px);
  grid-row-gap: .5em;
  grid-column-gap: 1em;
}

.container .item {
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/2.3.0/mustache.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="app" class="container">
</div>

<button id="add_el">Add element</button>

<template id="item_template">
  <div class="item" style="background: {{color}}">
    <p>{{ num }}</p>
    <p>
      <button class="del_el">Delete</button>
    </p>
  </div>
</template>

P.S. Or you can use grid-auto-rows in my particular example.

Andry
  • 16,172
  • 27
  • 138
  • 246
art-solopov
  • 4,289
  • 3
  • 25
  • 44
  • 11
    grid-template-columns: repeat(auto-fill, Xpx); ... finally I found it :) thank you! – Sandro Sep 06 '17 at 14:11
  • @Sandro I think I'd actually recommend using `grid-auto-rows` but AFAIK it wouldn't work if the "auto rows" aren't inserted last. Anyway, good luck! – art-solopov Sep 06 '17 at 14:29
  • There are `grid-auto-columns` too! – art-solopov Sep 06 '17 at 16:35
  • Yeah but I didn't get them to work to use a dynamic amount of colums. It just used one. – Sandro Sep 06 '17 at 17:25
  • 5
    Would setting `grid-auto-flow` to `column` help? https://codepen.io/art-solopov/pen/xLNYVw – art-solopov Sep 06 '17 at 21:33
  • I wanted something like this: https://codepen.io/sandrosc/pen/BdgoXN Sadly I can't set a max-width for columns (so that all vertical space would be used). – Sandro Sep 07 '17 at 16:05
  • The columns in this case shrink. Is there a way to add dynamic number of columns with auto widths and set overflow-x to scroll? – lohiarahul Oct 03 '20 at 14:38
80

Simply use below code to generate grid columns automatically

.container {
  display: grid;
  grid-auto-flow: column;
}
Alex Varghese
  • 2,000
  • 16
  • 17
  • 2
    but this change gives almost equal width. How can we make it to render with respect to child-column width. Say my first column is `220px`, and second column i want is `auto` or say `200px auto auto` or `220px auto 220px`? – Deepak Yadav Dec 28 '20 at 08:26
  • it is good idea to keep inline blocks without knowing the length and count – 3gwebtrain Apr 07 '21 at 16:38
  • This worked for me better than other answers cause I had a dynamic number of icons I wanted to show on a 'tray' container. – 10110 Jun 05 '22 at 00:14
  • Grid auto flow will kind of mess with the ordering in your layout. If you are trying to mimic something like a table using grid, this is not a good approach. – sin2akshay Nov 28 '22 at 12:26
23

For those landing here looking for a way to have a sort of dynamic table, with items wrapping to new rows, and still being aligned across rows, a pretty good solution is to use flex with flex-wrap and flex: 1 for all elements:

.container {
  width: 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
}

.container .item {
  flex: 1;
}
Vic Seedoubleyew
  • 9,888
  • 6
  • 55
  • 76
5

TLDR; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr))

What I came to look for was how to make the browser calculate the appropriate number of columns while keeping the rows stretched. I found my answer in the mdn docs.

The important thing is to set a column min width. In my example I used 200px.

.container {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}




.fancy-colors {
  border: 2px solid #f76707;
  border-radius: 5px;
  background-color: #fff4e6;
  resize: horizontal;
  overflow: auto;
  > * {
    border: 2px solid #ffa94d;
    border-radius: 5px;
    background-color: #ffd8a8;
    padding: 1em;
    color: #d9480f;
  }
}
<div class="container fancy-colors">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
  <div>Six</div>
  <div>Seven</div>
</div>
4

Something like this?

$(document).ready(function() {
 //Prepare Element selectors
  var cssElem = $("#Dynam"), rowElem = $("#rows"), columnElem = $("#columns"), appElem = $("#app");
  var noItems = $(".item").length,defaultColumns = 4;
    
  //Init default state
  cssElem.html(".container .item {flex: 0 0 calc(" + (100 / defaultColumns) + "% - 1em);}");
  columnElem.val(defaultColumns);
  rowElem.val(Math.ceil(noItems / columnElem.val()));

  //Add listeners to change
  appElem.on("DOMSubtreeModified", function() {
    noItems = $(".item").length;
    rowElem.val(Math.ceil(noItems / columnElem.val()));
  });
  columnElem.on("change", function() {
    rowElem.val(Math.ceil(noItems / columnElem.val()));
    cssElem.html(".container .item {flex: 0 0 calc(" + (100 / columnElem.val()) + "% - 1em);}");
  });
  rowElem.on("change", function() {
    columnElem.val(Math.ceil(noItems / rowElem.val()));
    cssElem.html(".container .item {flex: 0 0 calc(" + (100 / columnElem.val()) + "% - 1em);}");
  });
});

const COLORS = ['#FE9', '#9AF', '#F9A', "#AFA", "#FA7"];

function addItem(container, template) {
  let color = COLORS[_.random(COLORS.length - 1)];
  let num = _.random(10000);

  container.append(Mustache.render(template, {
    color,
    num
  }));
}

$(() => {
  const tmpl = $('#item_template').html()
  const container = $('#app');

  for (let i = 0; i < 5; i++) {
    addItem(container, tmpl);
  }

  $('#add_el').click(() => {
    addItem(container, tmpl);
  })

  container.on('click', '.del_el', (e) => {
    $(e.target).closest('.item').remove();
  });
});
.container {
  width: 100%;
  display: flex;
  flex-flow: row wrap;
  justify-content: flex-start;
}

.container .item {
  min-height: 120px;
  margin: 0.25em 0.5em;
  overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/2.3.0/mustache.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<style id="Dynam"></style>
<button id="add_el">Add element</button> rows:
<input id="rows" /> columns:<input id="columns" />

<div id="app" class="container">
</div>

<template id="item_template">
  <div class="item" style="background: {{color}}">
    <p>{{ num }}</p>
    <p>
      <button class="del_el">Delete</button>
    </p>
  </div>
</template>
Adam K.
  • 888
  • 6
  • 18
  • 1
    I'm sorry if I wasn't clear enough, I wanted to recreate the effect I had using the recently-created [CSS Grid layout.](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout) – art-solopov Mar 30 '17 at 11:57
  • Oh? That's really fresh http://caniuse.com/#feat=css-grid ... I am quite surprised that it is being adopted by majority of browsers at once. Well sorry, i have zero knowledge about css grid at the moment. – Adam K. Mar 30 '17 at 12:01
3

Here it is a very simple example of dynamic columns in grid

.container {
    display: grid;
    grid-template-columns: repeat(auto-fit, 250px);
    grid-row-gap: 5px;
    grid-column-gap: 5px;
    justify-content: center;
    width: 90%;
    margin: 0 auto;
    list-style: none;
}

.container>.item {
    padding: 5px;
    background-color: gray;
}
<div class="container">
    <div class="item">item1</div>
    <div class="item">item2</div>
    <div class="item">item3</div>
    <div class="item">item4</div>
</div>
Vasil Valchev
  • 5,701
  • 2
  • 34
  • 39
1

You don't need to use repeat. Instead you can just set a variable --grid-template-columns from your javascript code.

rootEl.style.setProperty('--grid-template-columns' theGridTemplateColumnsValue)

theGridTemplateColumnsValue is a string that can also contains other css variables. This way you can have a dynamic number of columns per row.

Florian G
  • 567
  • 1
  • 5
  • 15
  • 4
    The specification for CSS Grid has auto-fit, auto-fill, repeat, and also grid-auto-columns and grid-auto-rows to use, in collaboration with each other and flexbox in order to solve layout challenges without the need to bring JavaScript into the picture. CSS Grid was created for all two-dimensional layout challenges. (Flexbox is meant for flow control.) No JavaScript required. See answer below by the OP, as they discovered in the specification of CSS Grid. https://developer.mozilla.org/en-US/docs/Web/CSS/grid – weo3dev Jan 25 '20 at 02:59