Когда элементы flexbox обертываются в режиме столбцов, контейнер не увеличивает свою ширину

Я работаю над вложенным макетом flexbox, который должен работать следующим образом:

Самый внешний уровень (ul#main) - это горизонтальный список, который должен расширяться вправо, когда к нему добавляется больше элементов. Если он становится слишком большим, должна быть горизонтальная полоса прокрутки.

#main {
    display: flex;
    flex-direction: row;
    flex-wrap: nowrap;
    overflow-x: auto;
    /* ...and more... */
}

Каждый элемент этого списка (ul#main > li) имеет заголовок (ul#main > li > h2) и внутренний список (ul#main > li > ul.tasks). Этот внутренний список является вертикальным и должен при необходимости переноситься в столбцы. При обтекании большего количества столбцов его ширина должна увеличиваться, чтобы освободить место для большего количества элементов. Это увеличение ширины должно применяться также к содержащемуся элементу внешнего списка.

.tasks {
    flex-direction: column;
    flex-wrap: wrap;
    /* ...and more... */
}

Моя проблема в том, что внутренние списки не переносятся, когда высота окна становится слишком маленькой. Я много пытался изменить все свойства flex, стараясь неукоснительно следовать рекомендациям CSS-Tricks, но безуспешно.

Этот JSFiddle показывает, что я имею до сих пор.

Ожидаемый результат (чего я хочу):

My desired output

Фактический результат (что я получаю):

My current output

Более старый результат (что я получил в 2015 году):

My older output

ОБНОВИТЬ

После некоторого расследования это начинает выглядеть как большая проблема. Все основные браузеры ведут себя одинаково, и это не имеет ничего общего с моим дизайном flexbox. Еще более простые макеты столбцов flexbox отказываются увеличивать ширину списка при переносе элементов.

Этот другой JSFiddle ясно демонстрирует проблему. В текущих версиях Chrome, Firefox и IE11 все элементы переносятся правильно; высота списка увеличивается в режиме row, но ее ширина не увеличивается в режиме column. Кроме того, при изменении высоты column не происходит немедленного перекомпоновки элементов, но это происходит в режиме row.

Тем не менее, официальные спецификации (смотри, в частности, пример 5), похоже, указывают на то, что то, что я хочу сделать, должно быть возможным.

Может кто-нибудь придумать обходной путь к этой проблеме?

ОБНОВЛЕНИЕ 2

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

Сейчас я возвращаюсь к overflow-y: auto вместо flex-wrap: wrap чтобы внутренний контейнер прокручивался вертикально при необходимости. Это не красиво, но это один из способов продвижения вперед, который, по крайней мере, не сильно нарушает удобство использования.

Ответ 1

Эта проблема

Это похоже на фундаментальный недостаток гибкого макета.

Гибкий контейнер в направлении столбцов не будет расширяться для размещения дополнительных столбцов. (Это не проблема в flex-direction: row.)

Этот вопрос задавался много раз (см. Список ниже), без четких ответов в CSS.

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

Как это возможно, что во всех основных браузерах гибкий контейнер расширяется при переносе в направлении строки, а не в направлении столбца?

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

ОБНОВЛЕНИЕ: проблема, кажется, решена в Edge v16.


Иллюстрация проблемы

ОП создал полезную демонстрацию, иллюстрирующую проблему. Я копирую это здесь: http://jsfiddle.net/nwccdwLw/1/


Варианты обхода

Хакерские решения от сообщества Stack Overflow:


Больше анализа


Другие сообщения, описывающие ту же проблему

Ответ 2

Опоздал на вечеринку, но все еще сталкивался с этой проблемой ГОДАМИ позже. Закончилось поиском решения с использованием сетки. На контейнере вы можете использовать

display: grid;
grid-auto-flow: column;
grid-template-rows: repeat(6, auto);

У меня есть пример на CodePen, который переключается между проблемой flexbox и исправлением сетки: https://codepen.io/MandeeD/pen/JVLdLd

Ответ 3

Поскольку решения или правильного обходного пути пока не предлагалось, мне удалось получить требуемое поведение с немного другим подходом. Вместо того, чтобы разделять макет на 3 разных элемента, я добавляю все элементы в 1 блок и создаю разделение с несколькими дополнительными элементами.

Предлагаемое решение жестко запрограммировано, предполагая, что у нас есть 3 раздела, но может быть расширено до общего. Основная идея состоит в том, чтобы объяснить, как мы можем достичь этого макета.

  1. Добавление всех элементов в 1 контейнерный блок, который использует flex для упаковки элементов
  2. Первый элемент каждого "внутреннего контейнера" (я назову его разделом) будет иметь класс, который помогает нам выполнять некоторые манипуляции, которые создают разделение и стилизацию каждого раздела.
  3. Используя :before для каждого первого элемента, мы можем найти заголовок каждого раздела.
  4. Использование space создает разрыв между разделами
  5. Так как space не покрывает всю высоту раздела, я также добавляю :after к разделам, поэтому позиционируем его с абсолютным положением и белым фоном.
  6. Чтобы стилизовать цвет фона каждого раздела, я добавляю еще один div внутри первого элемента каждого раздела. Я также буду с абсолютным положением и буду иметь z-index: -1.
  7. Чтобы получить правильную ширину каждого фона, я использую JS, устанавливаю правильную ширину, а также добавляю слушателя для изменения размера.

function calcWidth() {
  var size = $(document).width();
  var end = $(".end").offset().left;

  var todoWidth = $(".doing-first").offset().left;
  $(".bg-todo").css("width", todoWidth);

  var doingWidth = $(".done-first").offset().left - todoWidth;
  $(".bg-doing").css("width", doingWidth);

  var doneWidth = $(".end").offset().left - $(".done-first").offset().left;
  $(".bg-done").css("width", doneWidth + 20);

}

calcWidth();

$(window).resize(function() {
  calcWidth();
});
.container {
  display: flex;
  flex-wrap: wrap;
  flex-direction: column;
  height: 120px;
  align-content: flex-start;
  padding-top: 30px;
  overflow-x: auto;
  overflow-y: hidden;
}

.item {
  width: 200px;
  background-color: #e5e5e5;
  border-radius: 5px;
  height: 20px;
  margin: 5px;
  position: relative;
  box-shadow: 1px 1px 5px 0px rgba(0, 0, 0, 0.75);
  padding: 5px;
}

.space {
  height: 150px;
  width: 10px;
  background-color: #fff;
  margin: 10px;
}

.todo-first:before {
  position: absolute;
  top: -30px;
  height: 30px;
  content: "To Do (2)";
  font-weight: bold;
}

.doing-first:before {
  position: absolute;
  top: -30px;
  height: 30px;
  content: "Doing (5)";
  font-weight: bold;
}

.doing-first:after,
.done-first:after {
  position: absolute;
  top: -35px;
  left: -25px;
  width: 10px;
  height: 180px;
  z-index: 10;
  background-color: #fff;
  content: "";
}

.done-first:before {
  position: absolute;
  top: -30px;
  height: 30px;
  content: "Done (3)";
  font-weight: bold;
}

.bg-todo {
  position: absolute;
  background-color: #FFEFD3;
  width: 100vw;
  height: 150px;
  top: -30px;
  left: -10px;
  z-index: -1;
}

.bg-doing {
  position: absolute;
  background-color: #EFDCFF;
  width: 100vw;
  height: 150px;
  top: -30px;
  left: -15px;
  z-index: -1;
}

.bg-done {
  position: absolute;
  background-color: #DCFFEE;
  width: 10vw;
  height: 150px;
  top: -30px;
  left: -15px;
  z-index: -1;
}

.end {
  height: 150px;
  width: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">

  <div class="item todo-first">
    <div class="bg-todo"></div>
    Drink coffee
  </div>
  <div class="item">Go to work</div>

  <div class="space"></div>

  <div class="item doing-first">
    <div class="bg-doing"></div>
    1
  </div>
  <div class="item">2</div>
  <div class="item">3</div>
  <div class="item">4</div>
  <div class="item">5</div>

  <div class="space"></div>

  <div class="item done-first">
    <div class="bg-done"></div>
    1
  </div>
  <div class="item">2</div>
  <div class="item">3</div>

  <div class="end"></div>

</div>

Ответ 4

Возможное решение JS..

var ul = $("ul.ul-to-fix");

if(ul.find("li").length>{max_possible_rows)){
    if(!ul.hasClass("width-calculated")){
        ul.width(ul.find("li").eq(0).width()*ul.css("columns"));
        ul.addClass("width-calculated");
     }
}