Сделайте так, чтобы контейнер сетки заполнял столбцы, а не строки

Я хочу, чтобы моя сетка заполнялась по вертикали следующим образом:

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 Grid? Я прочитал https://css-tricks.com/snippets/css/complete-guide-grid/, но ничего не видел о порядке.

CSS Flexbox имеет flex-direction, разве нет такого атрибута для CSS Grid?


person Glen Pierce    schedule 21.05.2017    source источник
comment
Это очень интересная проблема, у меня тот же вопрос, и мне интересно, нашли ли вы лучшее решение, чем ответы здесь, которые не подходят для произвольного количества элементов / строк (или не используют CSS Grid).   -  person benface    schedule 03.08.2017
comment
Ответ, который работает в глубине души: stackoverflow.com/a/44099977/984471   -  person Manohar Reddy Poreddy    schedule 23.11.2018


Ответы (5)


Для вертикальной сетки, которая создает новые столбцы по мере необходимости, а строки не определены, рассмотрите возможность использования Многоколоночный макет CSS (пример). CSS Grid Layout (по крайней мере, текущая реализация - уровень 1) не может выполнить это задача. Вот в чем проблема:

В CSS Grid Layout существует обратная связь между свойствами grid-auto-flow и grid-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: row и grid-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: column и grid-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

person Michael Benjamin    schedule 21.05.2017
comment
Хотелось бы, чтобы это сработало, но теперь он просто заканчивается в одной строке: codepen.io/glenpierce/pen / XRyevJ - person Glen Pierce; 21.05.2017
comment
Я понимаю, о чем вы говорите, мне было недостаточно ясно. Важно, чтобы у меня было 3 столбца. - person Glen Pierce; 21.05.2017
comment
Дополнительная информация: я создаю виджет, который будет использоваться в нескольких местах. Контейнеры могут быть любой высоты и должны содержать любое количество строк. Единственное ограничение, которое я пытаюсь наложить на сетку, состоит в том, что она должна быть шириной в 3 столбца и упорядочивать элементы сначала по столбцам, а затем по строкам. - person Glen Pierce; 21.05.2017
comment
Позвольте нам продолжить это обсуждение в чате. - person Glen Pierce; 21.05.2017

Другой вариант - отказаться от CSS Grid и использовать CSS Columns, что делает именно то, что вы просите, а также имеет гораздо лучшую поддержку браузера.

.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>

person Ason    schedule 21.05.2017
comment
Небольшое примечание: последние версии Chrome (50+) и Firefox (52+) уже имеют свойства, связанные со столбцами, без префиксов (developer.mozilla.org/en-US/docs/Web/CSS/) - person Ilya Streltsyn; 21.05.2017
comment
@IlyaStreltsyn Я знаю, но как количество пользователей, которые все еще используют старых версий довольно много, неплохо было бы оставить приставку подольше :) ¨ - person Ason; 21.05.2017
comment
Абсолютно верно .... В большинстве случаев вопрос не должен заключаться в том, как я могу заставить этот инструмент вести себя так, как я хочу, а в том, выбрал ли я правильный инструмент. - person vals; 22.05.2017
comment
@val, согласен, если, конечно, не менее важно, чтобы дочерние элементы принимали свойства сетки / гибкости. - person Michael Benjamin; 22.05.2017
comment
Да, столбцы CSS кажутся лучше всего подходящими для чисто текстового контента, а не блоков, из-за нестабильного поведения обертывания, которое может непреднамеренно разбивать блоки на части, с обработкой, отличной от кроссбраузерности. - person MattTreichel; 12.02.2019

Скорее как техническое упражнение, чем как практическое решение, вы можете каким-то образом получить свой результат, используя определенные стили в зависимости от количества элементов.

Посмотрим, как это работает:

.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)

и поместите их во вторую колонку. Аналогичное правило, теперь для 5-го и далее

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

помещает остальные элементы в третий столбец. Эти 2 правила имеют одинаковый приоритет и нацелены на оба последних элемента, поэтому очень важно, чтобы они появлялись в этом порядке.

Нам нужно повторить аналогичные правила до максимального количества элементов, которые могут присутствовать (возможно, это работа препроцессора)

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>

person vals    schedule 21.05.2017
comment
Удивительное использование : nth-child (). - person Michael Benjamin; 22.05.2017

Самый простой способ, который я видел, следующий:

.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>

person RayYates    schedule 09.01.2019
comment
Просто, да, но не работает, если вам нужно переменное количество строк, в зависимости от длины входящих данных - person mix3d; 09.03.2020

Вот один из подходов на основе CSS Grid с использованием 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>

person Rounin    schedule 06.06.2018