Когда элементы 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 контейнерный блок, который использует flex для упаковки элементов
- Первый элемент каждого "внутреннего контейнера" (я назову его разделом) будет иметь класс, который помогает нам выполнять некоторые манипуляции, которые создают разделение и стилизацию каждого раздела.
- Используя
:before
для каждого первого элемента, мы можем найти заголовок каждого раздела. - Использование
space
создает разрыв между разделами - Так как
space
не покрывает всю высоту раздела, я также добавляю :after
к разделам, поэтому позиционируем его с абсолютным положением и белым фоном. - Чтобы стилизовать цвет фона каждого раздела, я добавляю еще один div внутри первого элемента каждого раздела. Я также буду с абсолютным положением и буду иметь
z-index: -1
. - Чтобы получить правильную ширину каждого фона, я использую 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");
}
}