Flexbox: 4 предмета в ряду

260

Я использую flex box для отображения 8 элементов, которые будут динамически изменяться с моей страницей. Как заставить его разбить элементы на два ряда? (4 в ряду)?

Вот соответствующий фрагмент:

(Или, если вы предпочитаете jsfiddle - http://jsfiddle.net/vivmaha/oq6prk1p/2/ )

.parent-wrapper {
  height: 100%;
  width: 100%;
  border: 1px solid black;
}
.parent {
  display: flex;
  font-size: 0;
  flex-wrap: wrap;
  margin: -10px 0 0 -10px;
}
.child {
  display: inline-block;
  background: blue;
  margin: 10px 0 0 10px;
  flex-grow: 1;
  height: 100px;
}
<body>
  <div class="parent-wrapper">
    <div class="parent">
      <div class="child"></div>
      <div class="child"></div>
      <div class="child"></div>
      <div class="child"></div>
      <div class="child"></div>
      <div class="child"></div>
      <div class="child"></div>
      <div class="child"></div>
    </div>
  </div>
</body>

Вивек Махарадж
источник
16
[Обновление] Этот вопрос был основан на плохой неотзывчивый дизайн. Если вы обнаружите, что используете один из ответов ниже, будьте осторожны. В хорошем дизайне у вас будет больше элементов на более широких экранах и меньше на меньших экранах. Форсирование 4 пунктов для всех размеров экрана будет выглядеть привлекательно только в узком диапазоне ширины экрана.
Вивек Махарадж,

Ответы:

382

Вы попали flex-wrap: wrapна контейнер. Это хорошо, потому что оно переопределяет значение по умолчанию, которое nowrap( источник ). Это причина, по которой элементы в некоторых случаях не переносятся в сетку .

В этом случае основная проблема заключается flex-grow: 1в гибких элементах.

flex-growСвойство фактически не размер сгибать детали. Его задача - распределить свободное пространство в контейнере ( исходном ). Поэтому независимо от того, насколько мал размер экрана, каждый элемент получит пропорциональную часть свободного места в строке.

Точнее, в вашем контейнере есть восемь флекс. При этом flex-grow: 1каждый получает 1/8 свободного места на линии. Поскольку в ваших элементах нет содержимого, они могут сжиматься до нулевой ширины и никогда не переносятся.

Решение состоит в том, чтобы определить ширину элементов. Попробуй это:

.parent {
  display: flex;
  flex-wrap: wrap;
}

.child {
  flex: 1 0 21%; /* explanation below */
  margin: 5px;
  height: 100px;
  background-color: blue;
}
<div class="parent">
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
</div>

С участием flex-grow: 1 определенным в flexсокращении, нет необходимости flex-basisбыть 25%, что фактически привело бы к трем элементам в строке из-за полей.

Поскольку flex-growв строке будет занимать свободное место, он flex-basisдолжен быть достаточно большим, чтобы обеспечить перенос. В этом случае при flex-basis: 21%наличии достаточно места для полей, но никогда не хватает места для пятого элемента.

Майкл Бенджамин
источник
1
Мне это нравится, потому что это работает для меня, но «много места» заставляет меня чувствовать себя немного неловко. Есть ли какое-то крошечное окно или ситуация, когда эта «оценка» может меня подвести?
Джексон
1
Что за ситуация? В большинстве макетов есть крайние случаи, которые могут создавать проблемы. Такие случаи обычно проходят анализ затрат и выгод, потому что они никогда или почти никогда не происходят. Например, в приведенном выше решении, если вы добавите очень большие поля, макет может сломаться. Если это проблема, то она должна быть размещена в вопросе. @Джексон
Майкл Бенджамин
2
«Если вы добавите очень большие поля, макет может сломаться». Это пример сценария, которого я хотел бы избежать. Меня не волнует практичность; развлекать мое обожание к совершенству. Есть ли способ быть более точным?
Джексон,
27
Вы также можете использоватьflex: 1 0 calc(25% - 10px);
MarkG
2
Отличный ответ! Небольшое улучшение, которое я использую, margin: 2%;вместо фиксированного числа пикселей, чтобы блоки не переходили на новую строку на небольших устройствах / разрешениях. общая формула (100 - (4 * box-width-percent)) / 8% для разных
значений
104

Добавьте ширину к .childэлементам. Лично я бы использовал проценты, margin-leftесли вы хотите, чтобы оно всегда было 4 на строку.

DEMO

.child {
    display: inline-block;
    background: blue;
    margin: 10px 0 0 2%;
    flex-grow: 1;
    height: 100px;
    width: calc(100% * (1/4) - 10px - 1px);
}
dowomenfart
источник
Отличная идея. Я использовал эту идею с незначительными правками. jsfiddle.net/vivmaha/oq6prk1p/6
Вивек Махарадж
1
Ах. Это на самом деле не решает мою проблему. Смотрите здесь для примера: jsfiddle.net/vivmaha/oq6prk1p/7 . Я не могу использовать проценты для моей маржи. Они должны быть фиксированной ширины. Это означает, что выбранные нами 23% волшебства не сработают, поскольку не учитывают фиксированную маржу.
Вивек Махарадж
3
Да, Calc решает это! 'calc (100% * (1/4) - 10px - 1px) ". См. Jsfiddle.net/vivmaha/oq6prk1p/9
Вивек Махарадж
24
Тогда какая польза от гибкости в этом?
дважды
А как насчет адаптивного дизайна? Мы не хотим устанавливать фиксированную ширину для ребенка
подсвечник
40

Вот еще один apporach.

Вы также можете сделать это следующим образом:

.parent{
  display: flex;
  flex-wrap: wrap;
}

.child{
  width: 25%;
  box-sizing: border-box;
}

Образец: https://codepen.io/capynet/pen/WOPBBm

И более полный пример: https://codepen.io/capynet/pen/JyYaba

Capy
источник
1
А что, если у меня меньше 4 детей? Мне не нравится это пустое пустое пространство, ребенок не реагирует ...
подсвечник
В другом ответе Muddasir Abbas используются только гибкие настройки, поэтому я считаю, что это более чистое решение.
Эндрю Костер
17

Я бы сделал это так, используя отрицательные поля и calc для желобов:

.parent {
  display: flex;
  flex-wrap: wrap;
  margin-top: -10px;
  margin-left: -10px;
}

.child {
  width: calc(25% - 10px);
  margin-left: 10px;
  margin-top: 10px;
}

Демо: https://jsfiddle.net/9j2rvom4/


Альтернативный метод CSS Grid:

.parent {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-column-gap: 10px;
  grid-row-gap: 10px;
}

Демо: https://jsfiddle.net/jc2utfs3/

shanomurphy
источник
14
<style type="text/css">
.parent{
    display: flex;
    flex-wrap: wrap;
}
.parent .child{
    flex: 1 1 25%;
}
</style>    
<div class="parent">
      <div class="child"></div>
      <div class="child"></div>
      <div class="child"></div>
      <div class="child"></div>
      <div class="child"></div>
      <div class="child"></div>
      <div class="child"></div>
      <div class="child"></div>
    </div>

Надеюсь, поможет. для более подробной информации вы можете перейти по этой ссылке

Муддасир аббас
источник
4

Я считаю, что этот пример более прост и понятен, чем @dowomenfart.

.child {
    display: inline-block;
    margin: 0 1em;
    flex-grow: 1;
    width: calc(25% - 2em);
}

Это выполняет те же вычисления ширины при резке прямо на мясо. Математика намного проще и emявляется новым стандартом благодаря своей масштабируемости и мобильности.

Джозеф Чо
источник
3
на самом деле это не новый стандарт. Это может быть боль в заднице, и дизайнеры не проектируют с учетом EM.
Дэнни ван Холтен
2

.parent-wrapper {
	height: 100%;
	width: 100%;
	border: 1px solid black;
}
	.parent {
	display: flex;
	font-size: 0;
	flex-wrap: wrap;
	margin-right: -10px;
	margin-bottom: -10px;
}
	.child {
	background: blue;
	height: 100px;
	flex-grow: 1;
	flex-shrink: 0;
	flex-basis: calc(25% - 10px);
}
	.child:nth-child(even) {
	margin: 0 10px 10px 10px;
	background-color: lime;
}
	.child:nth-child(odd) {
	background-color: orange; 
}
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<style type="text/css">

	</style>
</head>
<body>
  <div class="parent-wrapper">
    <div class="parent">
      <div class="child"></div>
      <div class="child"></div>
      <div class="child"></div>
      <div class="child"></div>
      <div class="child"></div>
      <div class="child"></div>
      <div class="child"></div>
      <div class="child"></div>
    </div>
  </div>
</body>
</html>

;)

Матеус Маноссо Барщ
источник
1
Если вы можете добавить некоторый контекст о том, почему это отвечает на вопрос OP, а не просто публиковать код.
d219
если вы добавите justify-content: space-evenly;к prent, то они хорошо выровняются, и вам не нужно делать calcс ребенком. Спасибо за пример, хотя!
Mattijs
0

Flex wrap + отрицательное поле

Почему гибкий против display: inline-block?

Почему отрицательная маржа?

Либо вы используете SCSS или CSS-in-JS для крайних случаев (т. Е. Первый элемент в столбце), либо вы устанавливаете поле по умолчанию и позже избавляетесь от внешнего поля.

Реализация

https://codepen.io/zurfyx/pen/BaBWpja

<div class="outerContainer">
    <div class="container">
        <div class="elementContainer">
            <div class="element">
            </div>
        </div>
        ...
    </div>
</div>
:root {
  --columns: 2;
  --betweenColumns: 20px; /* This value is doubled when no margin collapsing */
}

.outerContainer {
    overflow: hidden; /* Hide the negative margin */
}

.container {
    background-color: grey;
    display: flex;
    flex-wrap: wrap;
    margin: calc(-1 * var(--betweenColumns));
}

.elementContainer {
    display: flex; /* To prevent margin collapsing */
    width: calc(1/var(--columns) * 100% - 2 * var(--betweenColumns));
    margin: var(--betweenColumns);
}

.element {
    display: flex;
    border: 1px solid red;
    background-color: yellow;
    width: 100%;
    height: 42px;
}
zurfyx
источник
-12

Вот еще один способ без использования calc().

// 4 PER ROW
// 100 divided by 4 is 25. Let's use 21% for width, and the remainder 4% for left & right margins...
.child {
  margin: 0 2% 0 2%;
  width: 21%;
}

// 3 PER ROW
// 100 divided by 3 is 33.3333... Let's use 30% for width, and remaining 3.3333% for sides (hint: 3.3333 / 2 = 1.66666)
.child {
  margin: 0 1.66666% 0 1.66666%;
  width: 30%;
}

// and so on!

Это все, что нужно сделать. Вы можете придумать размеры, чтобы получить более эстетичные размеры, но это идея.

wle8300
источник
6
Я не проверял это, но синтаксис CSS неправильный (значения CSS не заключаются в кавычки, а объявления должны заканчиваться точкой с запятой), поэтому он определенно не будет работать в своей текущей форме.
Ник Ф