Коли елементи flexbox загортаються в режимі стовпців, контейнер не збільшує свою ширину


167

Я працюю над вкладеним макетом флексокса, який повинен працювати наступним чином:

Зовнішній рівень ( 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 році) :

Мій старший вихід

ОНОВЛЕННЯ

Після деякого розслідування це починає виглядати як більша проблема. Усі основні браузери поводяться однаково , і це не має нічого спільного з тим, щоб мій гніздовий дизайн вкладений. Навіть простіші розкладки стовпців у флексі відмовляються збільшувати ширину списку при загортанні елементів.

Цей інший JSFiddle наочно демонструє проблему. У поточних версіях Chrome, Firefox та IE11 всі елементи завершуються правильно; висота списку збільшується в rowрежимі, але його ширина в columnрежимі не збільшується . Крім того, немає жодного негайного перезарядження елементів при зміні висоти columnрежиму, але він є в rowрежимі.

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

Чи може хтось вирішити цю проблему?

ОНОВЛЕННЯ 2

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

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


2
Просто сторонне позначення, але в Chrome є помилка, яка викликає контейнер з потоком, який column wrapне розширює його ширину при обгортанні. Для отримання додаткової інформації див. Code.google.com/p/chromium/isissue/detail?id=247963
Герріт Бертьє

Я також не можу змусити його працювати в IE11. Там загортають найпотаємніші предмети, але внутрішній список та його контейнер (зовнішній предмет) не збільшують їх ширину. В даний час я повертаюсь до того, що внутрішні списки прокручуються, але це не гарне довгострокове рішення, і я дуже хотів би уникати додавання JavaScript для цього.
Андерс Торнблад

Відповіді:


170

Проблема

Це виглядає як принциповий недолік у гнучкому розташуванні.

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

Це запитання задавали багато разів (див. Список нижче), без чітких відповідей у ​​CSS.

Важко зафіксувати це як помилку, оскільки проблема виникає у всіх основних браузерах. Але це викликає питання:

Як можливо, що всі основні браузери отримали гнучкий контейнер для розгортання на обгортці в напрямку рядка, але не в напрямку стовпця?

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

ОНОВЛЕННЯ: Здається, проблема вирішена в Edge v16.


Ілюстрація проблеми

ОП створила корисну демонстрацію проблеми. Я копіюю його тут: http://jsfiddle.net/nwccdwLw/1/


Параметри обходу

Хейкі рішення із спільноти Stack Overflow:


Більше аналізу


Інші пости, що описують ту саму проблему


2
Згідно з коментарями звітів про помилки, ця проблема не буде виправлена, доки вони не випустять свою нову версію
Ben.12,

5
Кількість посилань, які ви надали та класифікували, дійсно велика. Я хочу, щоб більшість людей писали відповіді таким чином. Чудова відповідь.
Виньєш

4
Ось корисний репо з переліком декількох помилок Flexbox та їх вирішення: Ця помилка занесена до №14
Ben.12

чи можна виправити цю проблему за допомогою сітки CSS?
Ісмаїл Фарук

ще один варіант вирішення: codepen.io/_mc2/pen/PoPmJRP Для певного аспекту я вважаю, що підхід у режимі запису
michalczerwinski

37

Запізнився на вечірку, але все ще стикався з цим питанням РОКІВ пізніше. Закінчили пошук рішення за допомогою сітки. На контейнері можна використовувати

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

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


1
Це елегантне вирішення, якщо я його коли-небудь бачив! Я просто потрапляю в сітку і люблю ваше рішення, дякую за це.
Альберт

Пляма на! Вирішили мою проблему.
Селестін Бохіс

Проблема все ще працює в Chrome. Я сподівався уникати використання сіткового вирішення. Добре знати, що інші використовують цей підхід!
trukvl

рятівник життя. Ви також можете використовувати, grid-row-end: span X;якщо вам потрібні деякі елементи, щоб зайняти більше рядків.
Meirion Hughes

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/isissue/detail?id=507397#c39, який привів мене до цього JSFiddle.


-1

Оскільки не було запропоновано жодного рішення чи належного вирішення, мені вдалося отримати запитувану поведінку трохи іншим підходом. Замість того, щоб розділяти макет на 3 різних діви, я додаю всі елементи в 1 div і створюю розділення ще кількома дівами між ними.

Запропоноване рішення важко закодовано, якщо припустити, що у нас є 3 розділи, але їх можна поширити на загальний. Основна ідея - пояснити, як ми можемо досягти цього планування.

  1. Додавання всіх елементів до 1 контейнера, який використовує flex для обгортання елементів
  2. Перший елемент кожного "внутрішнього контейнера" ​​(я називаю це секцією) матиме клас, який допомагає нам робити деякі маніпуляції, що створюють розділення та стилізацію кожного розділу.
  3. Використовуючи :beforeкожен перший елемент, ми можемо знайти заголовок кожного розділу.
  4. Використання spaceстворює зазор між секціями
  5. Оскільки spaceне буде охоплювати повну висоту розділу, я також додаю :afterдо секцій, таким чином розміщуючи його з абсолютним положенням та білим тлом.
  6. Для стилю кольору тла кожного розділу я додаю ще один роздільник всередині першого пункту кожного розділу. Я буду позицією і з абсолютною, і матиму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");
     }
}

На жаль, це не допоможе, оскільки висота окремих елементів може змінюватись. Тож кількість елементів не цікава ... Але використання columnsвластивості стилю може стати відправною точкою для робочого рішення. Можливо, регулюючи кількість стовпців та ширину вул.
Андерс Торнблад

1
Я також вирішив вирішити цю проблему JS для програми React, над якою працюю. Він призначений для роботи з елементами будь-якої і різної величини. Приклад тут: djskinner.github.io/react-column-wrap . Вихідний код тут: github.com/djskinner/react-column-wrap
djskinner
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.