Reputation: 1280
I have a dynamic number of elements (all of equal dimensions). I want to position them in a grid, such that they are ordered in columns, with a dynamic number of columns based on the available container width, and with a dynamic number of rows based on the number of elements.
For example, assuming there are 9 elements:
1 4 7
2 5 8
3 6 9
But if the container width expands:
1 3 5 7 9
2 4 6 8
Or shrinks:
1 6
2 7
3 8
4 9
5
This works fine when the items are positioned by row, but not by column. Also, if I explicitly set the number of rows, it works fine, but if I use auto-fill for both rows and columns, it just displays everything in a single row.
Here's a simple example I would expect to be rendered as a 3x3 grid: https://codepen.io/anon/pen/ZxPQNd
#grid {
width: 320px;
border: 1px solid red;
display: grid;
grid-gap: 10px;
grid-auto-flow: column;
grid-template-rows: repeat(auto-fill, 100px);
grid-template-columns: repeat(auto-fill, 100px);
grid-auto-columns: 100px;
grid-auto-rows: 100px;
}
#grid>div {
background: lime;
}
<div id="grid">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
</div>
Upvotes: 17
Views: 22747
Reputation: 114
The OP has the question exactly right. You've got an indeterminate number of elements to display and an indeterminately wide viewport in which to display them. And you want to display the elements in a grid.
Depending on the width of the viewport, you're going to want more or fewer columns. So the question is how to set that up.
One solution that comes to mind is to use CSS media queries to test the viewport width and then alter your layout accordingly. But that's tedious and prone to error. What you really want is a solution that works independently of viewport width.
The simplest solution, if all your elements are of uniformly fixed width, is to use flexbox. You might try something like this:
#grid {
display: flex;
flex-wrap: wrap;
}
#grid div {
flex: 1 0 110px;
background-color: yellow;
width: 100px;
height: 100px;
margin: 10px;
max-width: 100px;
text-align: center;
}
<div id="grid">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
</div>
But you might also want the elements to expand to fill the horizontal space while still maintaining a uniform grid. For this you can use a CSS grid. The trick is to use a repeat and a minmax on the grid-template-columns property:
#grid {
display: grid;
grid-auto-flow: row;
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
grid-template-rows: repeat(auto-fill, 1fr);
column-gap: 10px;
row-gap: 10px;
}
#grid div {
background-color: yellow;
height: 100px;
text-align: center;
}
<div id="grid">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
</div>
Upvotes: 3
Reputation: 14012
Your desired layout is achievable using CSS multi-column layout using column-width
property. Demo:
#grid {
border: 1px solid red;
column-width: 100px;
column-gap: 10px;
}
#grid > div {
break-inside: avoid-column; /* Prevent element from breaking */
page-break-inside: avoid; /* Prevent element from breaking in Firefox */
background: lime;
width: 100px;
height: 100px;
margin-bottom: 10px;
}
<div id="grid">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
</div>
Upvotes: 7
Reputation: 371371
This works fine when the items are positioned by row, but not by column.
This is because block elements consume the full width of their parent, by default.
But this behavior does not extend to height. Most elements are, by default, the height of their content (i.e., no extra space).
Essentially, your container is set to width: 100%
and height: auto
. For the row behavior in the vertical axis add height: 100vh
.
More info: How to make a div 100% height of the browser window?
#grid {
width: 320px;
border: 1px solid red;
display: grid;
grid-gap: 10px;
grid-auto-flow: column;
grid-template-rows: repeat(auto-fill, 100px);
grid-template-columns: repeat(auto-fill, 100px);
grid-auto-columns: 100px;
grid-auto-rows: 100px;
height: 100vh; /* NEW */
}
#grid > div {
background: lime;
}
<div id="grid">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
</div>
In terms of the re-ordering of grid items based on screen size, with grid-auto-flow: row
the items will respond to container width. With grid-auto-flow: column
, the items will respond to container height. To make the column-flowing items respond to width re-sizing will require some trickery.
More info: Make grid items fill columns not rows
Upvotes: 3