How to Calc() The Number of Rows in a Grid Template

How to calc() the number of rows in a grid template?

When using division with calc the result will be a number and not an integer thus it won't work because the repeat() expect an interger

The generic form of the repeat() syntax is, approximately,

repeat( [ <positive-integer> | auto-fill | auto-fit ] , <track-list> )ref

And

At /, check that the right side is <number>. If the left side is <integer>, resolve to <number>. Otherwise, resolve to the type of the left side.ref

And

Number values are denoted by <number>, and represent real numbers, possibly with a fractional component.ref

Even if we both know that the result will be an integer the browser will still consider it as a number.

You can have the same issue with multiplication in case you have a number in one of the sides

.grid {
display: grid;
grid-gap: 10px;
grid-template-columns: 1fr;
margin-bottom: 10px;
background: rgba(0, 0, 0, 0.2);
}

.grid>div {
background: tomato;
width: 20px;
text-align: center;
margin: auto;
}

.grid.no-calc {
grid-template-columns: repeat(3, 30px);
border-bottom:3px solid red;
}

.grid.multiplication {
grid-template-columns: repeat(calc(3 * 1.0), 30px); /*will fail*/
border-bottom:calc(3px * 1.0) solid red;
}

.grid.division {
grid-template-columns: repeat(calc(6 / 2), 30px);
border-bottom:calc(6px / 2) solid red; /*this will work because border accept numbers*/
}
<div class="grid no-calc">
<div>1</div>
<div>2</div>
<div>3</div>
</div>

<div class="grid multiplication">
<div>1</div>
<div>2</div>
<div>3</div>
</div>

<div class="grid division">
<div>1</div>
<div>2</div>
<div>3</div>
</div>

A way to count columns in a responsive grid

I think the easiest way without brute force is to consider a simple division. You can easily find the width of the container and each column is defined by minmax(300px,1fr) and we know the gap. Using all these information the calculation should be done like below:

If we will have N columns then we will have N-1 gaps. We also know that W should at least be 300px and cannot be more than a value (we will call Wmax).

Let's suppose the gap is equal to 10px.

If N=2 and each column is equal to 300px we will have the container width equal to 300px*2 + 10px*1 = 610px.

If N=3 and each column is equal to 300px we will have 300px*3 + 10px*2=920px.

Now it's clear that if the container width is between 610px and 920px we will have 2 columns because there is no space to hold 3 columns but enough space to hold 2 columns that we expand to fill the remaining space (using 1fr) so Wmax in this case is (920px - 10px)/2 = 455px. In other words, the width will vary from 300px to 455px when having 2 columns.

So if we take the formula 300px*N + 10px*(N-1) = Wc with Wc our container width you will see that N is equal to 2 when Wc=610px and 3 when Wc=920px and between we will have a result in [2,3] so we simply round the value to the smallest one (2 in this case) and we will have our column number.

Here is a basic example:

var gap = 10;var minW = 200;
var Wc = document.querySelector('.grid').offsetWidth;var N = Math.floor((Wc+gap)/(minW+gap));console.log(N);

window.addEventListener('resize', function(event){ Wc = document.querySelector('.grid').offsetWidth; N = Math.floor((Wc+gap)/(minW+gap)); console.log(N);});
.grid {  display:grid;  grid-template-columns:repeat(auto-fill,minmax(200px,1fr));  grid-gap:10px;}span {  min-height:50px;  background:red;}
<div class="grid">  <span></span>  <span></span>  <span></span>  <span></span>  <span></span>  <span></span></div>

Defining the number of rows in CSS grids

Simply define grid-auto-rows and you don't need to know the number of items

ul {  display: grid;  grid-auto-rows:1fr;  margin:10px;  padding:0;}ul li {  border:1px solid;  list-style-type:none;}
<ul>  <li>item <br>one</li>  <li>item two</li>  <li>item three</li></ul>
<ul> <li>item <br>one</li> <li>item two</li> <li>item three</li> <li>item two</li> <li>item three</li></ul>
<ul> <li>item <br>one</li> <li>item two</li></ul>

Specify Number of Rows and Columns in Grid

You can use grid-template-columns to specify the number of columns.

The number of columns is defined by the number of values in the list.

Below, I'm using repeat() as shorthand to generate four values.

Based on your existing code, auto auto auto auto would also work.

Also see CSS Grid Layout.

.grid-container {  display: grid;  grid-template-columns: repeat(4, 1fr);  background-color: #2196F3;  padding: 10px;}
.grid-item { background-color: rgba(255, 255, 255, 0.8); border: 1px solid rgba(0, 0, 0, 0.8); padding: 20px; font-size: 30px; text-align: center;}
<h1>Grid Elements</h1><div class="grid-container">  <div class="grid-item">1</div>  <div class="grid-item">2</div>  <div class="grid-item">3</div>  <div class="grid-item">4</div>  <div class="grid-item">5</div>  <div class="grid-item">6</div>  <div class="grid-item">7</div>  <div class="grid-item">8</div>  <div class="grid-item">9</div>  <div class="grid-item">10</div>  <div class="grid-item">11</div>  <div class="grid-item">12</div></div>

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

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>

How can I get an array of values or rows from my grid layout?

You can grab all the items using querySelectorAll and use getComputedStyle
to count the number of columns in each row. see this answer

var computedStyle = getComputedStyle(document.querySelector('.grid-table'))
var columnsCount = computedStyle.getPropertyValue("grid-template-columns").split(" ").length // in our case 5

var container = document.querySelector('.grid-table')
var columns = getComputedStyle(container).getPropertyValue("grid-template-columns").split(" ").length;

var items = document.querySelectorAll('.grid-table div');

var output = []
var row = 0;

items.forEach(function (item, i) {
if (i % columns === 0) {
if (output.length > 0) {
row++ ;
}

output.push([])
}

output[row].push(item.innerHTML)

});

console.log(output)
.grid-table {
display: grid;
grid-template-columns: repeat(5, 1fr);
}
<div class="grid-table">
<div>head1</div>
<div>head2</div>
<div>head3</div>
<div>head4</div>
<div>head5</div>
<div>item1</div>
<div>item2</div>
<div>item3</div>
<div>item4</div>
<div>item5</div>
<div>item6</div>
<div>item7</div>
<div>item8</div>
<div>item9</div>
</div>

Change the number of columns and rows in a grid as the number of items increase

Implicit columns creation can do this. You can consider nth-child()/nth-last-child() to create a new column when you reach a specific number of columns:

.container {
display: inline-grid;
width: 100px;
vertical-align: top;
border: 1px solid;
}
.container > :nth-child(2) {
grid-column: 2;
}

.container > :nth-last-child(n + 5) ~ :nth-child(3) {
grid-column: 3;
}

.container > :nth-last-child(n + 10) ~ :nth-child(4) {
grid-column: 4;
}

.container > :nth-last-child(n + 17) ~ :nth-child(5) {
grid-column: 5;
}

.container > * {
border: 1px solid red;
aspect-ratio: 1;
}
<div class="container">
<div></div>
</div>

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Can I use css-grid to display an unknown number of items, in left-to-right reading order, over two rows?

A CSS only solution where you need to write a bit of CSS to cover many cases. The below code covers up to 12 elements and the CSS isn't that big. You can keep adding more to cover more cases.

.box {
display: grid;
grid-auto-flow:columns;
grid-auto-columns:1fr;
counter-reset: divs;
margin: 5px;
border:1px solid;
}

.box div {
border:1px solid red;
}

.box div:before {
counter-increment: divs;
content: counter(divs);
}

.box div:nth-child(1):nth-last-child(2) ~ *,
.box div:nth-child(2):nth-last-child(3) ~ *,
.box div:nth-child(3):nth-last-child(4) ~ *,
.box div:nth-child(4):nth-last-child(5) ~ *,
.box div:nth-child(5):nth-last-child(6) ~ *,
.box div:nth-child(6):nth-last-child(7) ~ *
/*
.box div:nth-child(N):nth-last-child(N+1) ~ *
*/{
grid-row:2;
}
<div class="box">
<div></div>
</div>

<div class="box">
<div></div>
<div></div>
</div>

<div class="box">
<div></div>
<div></div>
<div></div>
<div></div>
</div>

<div class="box">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>

<div class="box">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>

<div class="box">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>

<div class="box">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>


Related Topics



Leave a reply



Submit