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

168

Я работаю над вложенным макетом 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 показывает, что я имею до сих пор.

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

Мой желаемый выход

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

Мой текущий вывод

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

Мой старый вывод

ОБНОВИТЬ

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

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

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

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

ОБНОВЛЕНИЕ 2

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

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

Андерс Торнблад
источник
2
Просто sidenote, но в Chrome есть ошибка, из-за которой контейнер с установленным потоком column wrapне расширяет свою ширину при переносе. См. Code.google.com/p/chromium/issues/detail?id=247963 для получения дополнительной информации.
Геррит Бертье
Я не могу заставить его работать в IE11. Там самые внутренние элементы переносятся, но внутренний список и его контейнер (внешний элемент) не увеличивают свою ширину. В настоящее время я возвращаюсь к прокрутке внутренних списков, но это не является хорошим долгосрочным решением, и я действительно хотел бы избежать добавления JavaScript для этого.
Андерс Торнблад,

Ответы:

171

Эта проблема

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

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

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

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

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

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

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


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

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


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

Решения от сообщества Stack Overflow:


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


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

Майкл Бенджамин
источник
2
Согласно комментариям к отчету об ошибке, эта проблема не будет исправлена, пока они не выпустят свой новый механизм компоновки LayoutNG
Ben.12
5
Количество ссылок, которые вы предоставили и классифицировали, действительно велико. Я бы хотел, чтобы большинство людей писали ответы таким образом. Отличный ответ.
Vignesh
4
Вот полезный репозиторий, в котором перечислены несколько ошибок
Flexbox и способы
мы можем исправить эту проблему с помощью сетки CSS?
Исмаил Фарук
еще один обходной вариант: codepen.io/_mc2/pen/PoPmJRP Для некоторых аспектов я считаю, что лучше подход с режимом записи
michalczerwinski
38

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

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

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

MandeeD
источник
1
Это элегантный обходной путь, если я когда-либо видел его! Я только вхожу в сетку, и мне нравится ваше решение, спасибо за это.
Альберт
Пятно на! Решил мою проблему.
Селестин Бочис
Все еще сталкиваюсь с этой проблемой в Chrome. Я надеялся избежать использования обходного пути сетки. Приятно знать, что другие используют этот подход!
Труквл
спасатель. Вы также можете использовать, grid-row-end: span X;если вам нужно, чтобы некоторые элементы занимали больше строк.
Мейрион Хьюз
0

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

    element.style.flexBasis = "auto";
    element.style.flexBasis = `${element.scrollWidth}px`;
Стив Ханов
источник
0

Я только что нашел действительно потрясающий обходной путь PURE CSS здесь.

https://jsfiddle.net/gcob492x/3/

Хитрость: установите writing-mode: vertical-lrв списке div, затем writing-mode: horizontal-tbв элементе списка. Мне пришлось настроить стили в JSFiddle (удалить много стилей выравнивания, которые не нужны для решения).

Примечание: комментарий говорит, что он работает только в браузерах на основе Chromium, а не в Firefox. Я только лично тестировал в Chrome. Возможно, есть способ изменить это, чтобы он работал в других браузерах, или были обновления для указанных браузеров, которые делают эту работу.

Большой комментарий к этому комментарию: когда элементы flexbox переносятся в режиме столбца, контейнер не увеличивается в ширину . Изучение этой темы привело меня к https://bugs.chromium.org/p/chromium/issues/detail?id=507397#c39, который привел меня к этому JSFiddle.

Дастин Кейн
источник
-1

Поскольку решения или правильного обходного пути пока не предлагалось, мне удалось получить требуемое поведение с немного другим подходом. Вместо того, чтобы разделять макет на 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>

Итай Гал
источник
-2

Возможное решение 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");
     }
}
user2323336
источник
К сожалению, это не поможет, поскольку высота отдельных элементов может варьироваться. Так что количество элементов не интересно ... Но использование columnsсвойства стиля может стать отправной точкой для рабочего решения. Возможно корректировка количества колонок и ширины ул.
Андерс Торнблад
1
Я также решил использовать решение JS для этой проблемы для приложения React, над которым я работал. Он предназначен для работы с элементами любого и различного размера. Пример здесь: djskinner.github.io/react-column-wrap . Исходный код здесь: github.com/djskinner/react-column-wrap
djskinner