Can I make a CSS grid with dynamic number of rows or columns?
Asked Answered
I

7

148

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.

Intake answered 30/3, 2017 at 10:55 Comment(2)
do you wanna always have 4 cells per row or do you wanna to have those cells dynamic per row ? UnclearReste
Dynamic cells per row would be cool, but I'm content with having 4 cells per row right now.Intake
I
100

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.

Intake answered 30/3, 2017 at 12:10 Comment(7)
grid-template-columns: repeat(auto-fill, Xpx); ... finally I found it :) thank you!Suppose
@Suppose 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!Intake
There are grid-auto-columns too!Intake
Yeah but I didn't get them to work to use a dynamic amount of colums. It just used one.Suppose
Would setting grid-auto-flow to column help? codepen.io/art-solopov/pen/xLNYVwIntake
I wanted something like this: codepen.io/sandrosc/pen/BdgoXN Sadly I can't set a max-width for columns (so that all vertical space would be used).Suppose
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?Pyretic
H
84

Simply use below code to generate grid columns automatically

.container {
  display: grid;
  grid-auto-flow: column;
}
Haggai answered 29/5, 2020 at 13:21 Comment(4)
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?Dissert
it is good idea to keep inline blocks without knowing the length and countVolnay
This worked for me better than other answers cause I had a dynamic number of icons I wanted to show on a 'tray' container.Bosanquet
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.Wira
F
22

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;
}
Fredricfredrick answered 10/2, 2020 at 16:8 Comment(3)
Can also be done with css grid following this nice article: css-tricks.com/…Biocatalyst
@UriKlar You should really post that as a reply, that is a much cleaner solutionGulf
Good solution for a collection of card like my E-commerce project.Asymptote
O
9

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.

Fun fact: Instead of using 200px, you can use min(200px, 100%) which lets the items shrink even smaller when the container has not enough space. Similar to min-width: 100%.

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

.container:has(+input:checked) {
  grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr));
}


.fancy-colors {
  border: 2px solid #f76707;
  border-radius: 5px;
  background-color: #fff4e6;
  width: 600px;
  resize: horizontal;
  overflow: auto;
  position: relative;
  &::after {
    position: absolute;
    bottom: 0;
    right: 10px;
    content: "Resize me!";
    color: grey;
    font-size: 0.8em;
  }
  > * {
    border: 2px solid #ffa94d;
    border-radius: 5px;
    background-color: #ffd8a8;
    padding: 0.8em;
    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>

<input type="checkbox">min(200px, 100%)</input>
Oran answered 2/12, 2022 at 12:57 Comment(0)
S
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>
Sande answered 30/3, 2017 at 11:22 Comment(2)
I'm sorry if I wasn't clear enough, I wanted to recreate the effect I had using the recently-created CSS Grid layout.Intake
Oh? That's really fresh 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.Sande
S
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>
Seanseana answered 20/7, 2022 at 13:24 Comment(0)
P
2

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.

Provencher answered 21/10, 2019 at 17:29 Comment(1)
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. developer.mozilla.org/en-US/docs/Web/CSS/gridLietuva

© 2022 - 2024 — McMap. All rights reserved.