使网格容器填充列而不是行

我想让我的网格垂直填充,像这样:

1 4 7
2 5 8
3 6 9
... arbitrary number of additional rows.

相反,它像这样水平填充:

1 2 3
4 5 6
7 8 9

我希望指定网格中的列数,而不是行数。

下面是我的 div 使用内联 CSS 样式时的样子:

<div style="display:grid; grid-template-columns:1fr 1fr 1fr;">
<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>

重要的是,我的网格是3列宽,但我希望项填充的列,而不是行。这在 CSS 网格中可行吗?我已经通读了这个 https://css-tricks.com/snippets/css/complete-guide-grid/,但没有看到任何关于秩序。

CSS Flexbox 有 flex-direction,CSS 网格不是有这样的属性吗?

70779 次浏览

对于必要时创建新列且未定义行的垂直流网格,可以考虑使用 多列布局(例子)。CSS 网格布局(至少当前的实现 -第一层)不能执行此任务。问题是:

在 CSS 网格布局中,grid-auto-flowgrid-template-rows/grid-template-columns属性之间存在反比关系。

更具体地说,在同时定义了 grid-auto-flow: row(默认设置)和 grid-template-columns的情况下,网格项很好地沿着水平方向流动,根据需要自动创建新行。这个概念在问题中的代码中进行了说明。

#container {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-auto-flow: row;
}
<div id="container">
<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>

但是,如果切换到 grid-template-rows,网格项将堆叠在一个列中。

#container {
display: grid;
grid-template-rows: 1fr 1fr 1fr;
grid-auto-flow: row;
}
<div id="container">
<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>

使用 grid-auto-flow: rowgrid-template-rows不会自动创建列。必须定义 grid-template-columns(因此,与 grid-auto-flow的逆关系)。

#container {
display: grid;
grid-template-rows: 1fr 1fr 1fr;
grid-template-columns: 1fr 1fr 1fr;
grid-auto-flow: row;
}
<div id="container">
<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>

在相反的情况下,同样的行为是正确的。

在定义了 grid-auto-flow: columngrid-template-rows之后,网格项很好地沿垂直方向流动,根据需要自动创建新列。

#container {
display: grid;
grid-template-rows: 1fr 1fr 1fr;
grid-auto-flow: column;
}
<div id="container">
<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>

但是,如果切换到 grid-template-columns,网格项将堆叠在一行中。(这是大多数人都会问的问题,包括这个问题。)

#container {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-auto-flow: column;
}
<div id="container">
<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>

不会自动创建行。这需要定义 grid-template-rows。(这是最常提供的解决方案,但通常会被拒绝,因为布局的行数是可变的。)

#container {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr;
grid-auto-flow: column;
}
<div id="container">
<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>

因此,按照上面的建议,考虑 多列布局解决方案

规格参考: 7.7. 自动放置: grid-auto-flow属性

更多的是作为一个技术练习而不是一个实际的解决方案,您可以通过某种方式使用特定的样式来获得结果,这取决于项目的数量

让我们看看它是如何工作的:

.item:first-child:nth-last-child(n+4):nth-last-child(-n + 6) ~ .item:nth-child(n+3)

第一个选择器

.item:first-child:nth-last-child(n+4):nth-last-child(-n + 6)

是活动的,我们的列表有4到6个元素。在这种情况下,第一个条件和第二个条件都包含某个项。

在这种情况下,我们希望第一列中有2个项。其余项目(由第三项开始)

~ .item:nth-child(n+3)

把它们放在第二列。一个类似的规则,现在是第五列,以后也是

~ .item:nth-child(n+5)

将其他项放在第三列中。这两个规则具有相同的优先级,并且针对最后两个项目,因此它们以这种顺序出现是至关重要的。

我们需要重复类似的规则,直到可以显示的最大数量的项目(可能是预处理器的作业)

var elements = 5;


function add () {
var ctn = document.getElementById("container");
var ele = document.createElement("div");
elements ++;
ele.innerHTML = elements;
ele.className = "item";
ctn.appendChild (ele);
}
#container {
width: 90%;
border: solid 1px red;
display: grid;
grid-template-rows: 33% 33% 33%;
grid-auto-flow: column dense;
}


.item {
width: 90%;
height: 80px;
background-color: lightgreen;
margin: 10px;
grid-column: 1;
}


.item:first-child:nth-last-child(n+4):nth-last-child(-n + 6) ~ .item:nth-child(n+3) {
background-color: yellow;
grid-column: 2;
}


.item:first-child:nth-last-child(n+4):nth-last-child(-n + 6) ~ .item:nth-child(n+5) {
background-color: tomato;
grid-column: 3;
}


.item:first-child:nth-last-child(n+7):nth-last-child(-n + 9) ~ .item:nth-child(n+4) {
background-color: burlywood;
grid-column: 2;
}


.item:first-child:nth-last-child(n+7):nth-last-child(-n + 9) ~ .item:nth-child(n+7) {
background-color: blueviolet;
grid-column: 3;
}


.item:first-child:nth-last-child(n+10):nth-last-child(-n + 12) ~ .item:nth-child(n+5) {
background-color: darkcyan;
grid-column: 2;
}


.item:first-child:nth-last-child(n+10):nth-last-child(-n + 12) ~ .item:nth-child(n+9) {
background-color: chartreuse;
grid-column: 3;
}


.item:first-child:nth-last-child(n+13):nth-last-child(-n + 15) ~ .item:nth-child(n+6) {
background-color: yellow;
grid-column: 2;
}


.item:first-child:nth-last-child(n+13):nth-last-child(-n + 15) ~ .item:nth-child(n+11) {
background-color: tomato;
grid-column: 3;
}
<button onclick="add()">Add</button>
<div id="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>

另一个选择是删除 CSS 网格,使用 CSS 列,它可以完全满足您的要求,并且有更好的浏览器支持。

.csscolumn {
-webkit-column-count: 3;  /* Chrome, Safari, Opera */
-moz-column-count: 3;     /* Firefox */
column-count: 3;
}


/* styling for this demo */
.csscolumn {
width: 50%;
}
.csscolumn + .csscolumn {
margin-top: 10px;
padding-top: 10px;
border-top: 1px solid;
}
<div class="csscolumn">
<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>


<div class="csscolumn">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
</div>


<div class="csscolumn">
<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>10</div>
</div>

下面是一种基于 CSS 网格的方法,使用 javascript 和 CSSOM 插入一对:

transform: translate(x, y)

规则转换为生成的样式表。

这两个转换规则(有 只有两个,基于网格是 3列宽移动原来单列网格的下面的元素,向上和向右移动元素。

因此,可以向单列网格中添加任意数量的元素,脚本将始终调整网格,使其具有大小大致相等的三列。

如果列的大小不能完全相同,那么较高的列将始终是第一列和/或第二列(从不是最右边的第三列)。

工作示例(9个网格单元) :

var numberOfColumns = 3;


document.head.appendChild(document.createElement('style'));
var newStyles = document.styleSheets[(document.styleSheets.length - 1)];


var myGrid = document.getElementsByClassName('my-grid')[0];
var myGridUnits = myGrid.getElementsByTagName('div');


var tallColumn = Math.ceil(myGridUnits.length /  numberOfColumns);
var shortColumn = Math.floor(myGridUnits.length / numberOfColumns);


var nextUnit = 1;
var unitsRemaining = myGridUnits.length;
var xTranslate, yTranslate;
var columns = [];


for (var i = 0; i < (numberOfColumns - 1); i++) {




if (unitsRemaining % shortColumn === 0) {
    

columns.push(shortColumn);
}


else {


columns.push(tallColumn);
}
    

nextUnit += columns[(columns.length - 1)];
unitsRemaining -= columns[(columns.length - 1)];
    

xTranslate = ((i + 1) * 48);
yTranslate = 0;
columns.forEach(function(columnHeight){yTranslate += (columnHeight * 48);});
                         

newStyles.insertRule('.my-grid div:nth-of-type(n+' + nextUnit + ') {transform: translate(' + xTranslate + 'px, ' + (0 - (yTranslate)) + 'px);}', newStyles.cssRules.length);


}
.my-grid {
display: inline-grid;
grid-row-gap: 6px;
}


.my-grid div {
width: 40px;
height: 40px;
line-height: 40px;
text-align: center;
border: 1px solid rgb(127, 127, 127);
}
<div class="my-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>


工作示例(10个网格单元) :

var numberOfColumns = 3;


document.head.appendChild(document.createElement('style'));


var newStyles = document.styleSheets[(document.styleSheets.length - 1)];


var myGrid = document.getElementsByClassName('my-grid')[0];
var myGridUnits = myGrid.getElementsByTagName('div');


var tallColumn = Math.ceil(myGridUnits.length /  numberOfColumns);
var shortColumn = Math.floor(myGridUnits.length / numberOfColumns);


var nextUnit = 1;
var unitsRemaining = myGridUnits.length;
var xTranslate, yTranslate;
var columns = [];


for (var i = 0; i < (numberOfColumns - 1); i++) {




if (unitsRemaining % shortColumn === 0) {
    

columns.push(shortColumn);
}


else {


columns.push(tallColumn);
}
    

nextUnit += columns[(columns.length - 1)];
unitsRemaining -= columns[(columns.length - 1)];
    

xTranslate = ((i + 1) * 48);
yTranslate = 0;
columns.forEach(function(columnHeight){yTranslate += (columnHeight * 48);});
                         

newStyles.insertRule('.my-grid div:nth-of-type(n+' + nextUnit + ') {transform: translate(' + xTranslate + 'px, ' + (0 - (yTranslate)) + 'px);}', newStyles.cssRules.length);


}
.my-grid {
display: inline-grid;
grid-row-gap: 6px;
}


.my-grid div {
width: 40px;
height: 40px;
line-height: 40px;
text-align: center;
border: 1px solid rgb(127, 127, 127);
}
<div class="my-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>10</div>
</div>


工作示例(11个网格单元) :

var numberOfColumns = 3;


document.head.appendChild(document.createElement('style'));


var newStyles = document.styleSheets[(document.styleSheets.length - 1)];


var myGrid = document.getElementsByClassName('my-grid')[0];
var myGridUnits = myGrid.getElementsByTagName('div');


var tallColumn = Math.ceil(myGridUnits.length /  numberOfColumns);
var shortColumn = Math.floor(myGridUnits.length / numberOfColumns);


var nextUnit = 1;
var unitsRemaining = myGridUnits.length;
var xTranslate, yTranslate;
var columns = [];


for (var i = 0; i < (numberOfColumns - 1); i++) {




if (unitsRemaining % shortColumn === 0) {
    

columns.push(shortColumn);
}


else {


columns.push(tallColumn);
}
    

nextUnit += columns[(columns.length - 1)];
unitsRemaining -= columns[(columns.length - 1)];
    

xTranslate = ((i + 1) * 48);
yTranslate = 0;
columns.forEach(function(columnHeight){yTranslate += (columnHeight * 48);});
                         

newStyles.insertRule('.my-grid div:nth-of-type(n+' + nextUnit + ') {transform: translate(' + xTranslate + 'px, ' + (0 - (yTranslate)) + 'px);}', newStyles.cssRules.length);


}
.my-grid {
display: inline-grid;
grid-row-gap: 6px;
}


.my-grid div {
width: 40px;
height: 40px;
line-height: 40px;
text-align: center;
border: 1px solid rgb(127, 127, 127);
}
<div class="my-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>10</div>
<div>11</div>
</div>

工作示例(14个网格单元) :

var numberOfColumns = 3;


document.head.appendChild(document.createElement('style'));


var newStyles = document.styleSheets[(document.styleSheets.length - 1)];


var myGrid = document.getElementsByClassName('my-grid')[0];
var myGridUnits = myGrid.getElementsByTagName('div');


var tallColumn = Math.ceil(myGridUnits.length /  numberOfColumns);
var shortColumn = Math.floor(myGridUnits.length / numberOfColumns);


var nextUnit = 1;
var unitsRemaining = myGridUnits.length;
var xTranslate, yTranslate;
var columns = [];


for (var i = 0; i < (numberOfColumns - 1); i++) {




if (unitsRemaining % shortColumn === 0) {
    

columns.push(shortColumn);
}


else {


columns.push(tallColumn);
}
    

nextUnit += columns[(columns.length - 1)];
unitsRemaining -= columns[(columns.length - 1)];
    

xTranslate = ((i + 1) * 48);
yTranslate = 0;
columns.forEach(function(columnHeight){yTranslate += (columnHeight * 48);});
                         

newStyles.insertRule('.my-grid div:nth-of-type(n+' + nextUnit + ') {transform: translate(' + xTranslate + 'px, ' + (0 - (yTranslate)) + 'px);}', newStyles.cssRules.length);


}
.my-grid {
display: inline-grid;
grid-row-gap: 6px;
}


.my-grid div {
width: 40px;
height: 40px;
line-height: 40px;
text-align: center;
border: 1px solid rgb(127, 127, 127);
}
<div class="my-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>10</div>
<div>11</div>
<div>12</div>
<div>13</div>
<div>14</div>
</div>

我见过的最简单的方法如下:

.grid {
display: grid;
grid-auto-flow: column;
grid-gap: 1px;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(5, auto);
}
<div class="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>10</div>
<div>11</div>
<div>12</div>
<div>13</div>
</div>

垂直填充网格的一种方法是使用 column-count = 3; CSS 属性并删除 display: grid;。但是在我的案例中,它在 Iphone13上引起了一个问题(大的空白)。 下面是我如何在网格布局中将顺序从水平切换到垂直:

function horizontalToVerticalGridSorting(gridSelector) {
const grid = document.querySelector(gridSelector);
const gridItems = grid.children;
const gridComputedStyle = window.getComputedStyle(grid);
// Grid columns count
const colCount = gridComputedStyle.gridTemplateColumns.split(' ').length;
// Grid rows count
const rowsCount = gridComputedStyle.gridTemplateRows.split(' ').length;
// Total items count
const itemsCount = gridItems.length;
// Full columns amount
const fullColCount = itemsCount % colCount || colCount;
// Amount of items in full columns
const fullColItems = fullColCount*rowsCount;
// Current column
let curX = 1;
// Current row
let curY = 1;
for (let i = 0; i < itemsCount; i++) {
gridItems[i].style.order = (curY - 1) * colCount + curX;
// Number of items in column
let perColItems = rowsCount;
// Not full column
if (i >= fullColItems) {
perColItems--;
}
curY++;
// Switching to the next column
if (curY > perColItems) {
curY = 1;
curX++;
}
}
}
horizontalToVerticalGridSorting('.cols');
.cols {
text-align: center;
display: grid;
grid-template-columns: repeat(4, 1fr);
}
.col {
padding: 10px;
}
<div class="cols">
<div class="col">1</div>
<div class="col">2</div>
<div class="col">3</div>
<div class="col">4</div>
<div class="col">5</div>
<div class="col">6</div>
<div class="col">7</div>
<div class="col">8</div>
<div class="col">9</div>
<div class="col">10</div>
</div>

该函数遍历所有网格项并计算新坐标[ X,Y ]以进行垂直排序。然后根据新的坐标计算顺序。 它适用于任何网格大小