Kiedy elementy flexbox zawijają się w trybie kolumnowym, kontener nie zwiększa swojej szerokości

167

Pracuję nad zagnieżdżonym układem flexbox, który powinien działać w następujący sposób:

Najbardziej zewnętrzny poziom ( ul#main) to lista pozioma, która musi zostać rozszerzona w prawo, gdy zostanie do niej dodanych więcej elementów. Jeśli będzie zbyt duży, powinien pojawić się poziomy pasek przewijania.

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

Każda pozycja tej listy ( ul#main > li) ma nagłówek ( ul#main > li > h2) i wewnętrzną listę ( ul#main > li > ul.tasks). Ta wewnętrzna lista jest pionowa i w razie potrzeby powinna być zawijana w kolumny. Przy zawijaniu w więcej kolumn jego szerokość powinna się zwiększyć, aby zrobić miejsce na więcej elementów. To zwiększenie szerokości powinno dotyczyć również pozycji zawierającej wykaz zewnętrzny.

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

Mój problem polega na tym, że wewnętrzne listy nie zawijają się, gdy wysokość okna jest zbyt mała. Próbowałem wielu zmian we wszystkich właściwościach flex, starając się skrupulatnie przestrzegać wytycznych CSS-Tricks , ale bez powodzenia.

Ten JSFiddle pokazuje, co mam do tej pory.

Oczekiwany wynik (czego chcę) :

Moje pożądane wyjście

Rzeczywisty wynik (co otrzymuję) :

Moje aktualne wyjście

Starszy wynik (jaki uzyskałem w 2015 roku) :

Moje starsze dzieło

AKTUALIZACJA

Po pewnym dochodzeniu zaczyna wyglądać na większy problem. Wszystkie główne przeglądarki zachowują się w ten sam sposób i nie ma to nic wspólnego z zagnieżdżeniem mojego projektu flexbox. Jeszcze prostsze układy kolumn flexbox odmawiają zwiększenia szerokości listy, gdy elementy są zawijane.

Ten inny JSFiddle wyraźnie pokazuje problem. W aktualnych wersjach przeglądarek Chrome, Firefox i IE11 wszystkie elementy są poprawnie zawijane; wysokość listy wzrasta w rowtrybie, ale jej szerokość nie zwiększa się w columntrybie. Ponadto nie ma żadnego natychmiastowego ponownego przepływu elementów podczas zmiany wysokości columntrybu, ale jest wrow trybie.

Jednak oficjalne specyfikacje (spójrz konkretnie na przykład 5) wydają się wskazywać, że to, co chcę zrobić, powinno być możliwe.

Czy ktoś może wymyślić obejście tego problemu?

AKTUALIZACJA 2

Po wielu eksperymentach z wykorzystaniem JavaScript do aktualizowania wysokości i szerokości różnych elementów podczas zdarzeń zmiany rozmiaru, doszedłem do wniosku, że próba rozwiązania tego problemu w ten sposób jest zbyt złożona i zbyt trudna. Ponadto dodanie JavaScript zdecydowanie łamie model flexbox, który powinien być tak czysty, jak to tylko możliwe.

Na razie cofam się do overflow-y: autozamiast, aby w flex-wrap: wraprazie potrzeby wewnętrzny pojemnik przewijał się pionowo. Nie jest ładna, ale jest to jedna z możliwości, która przynajmniej nie narusza zbytnio użyteczności.

Anders Tornblad
źródło
2
To tylko notatka boczna, ale w Chrome jest błąd, który powoduje, że kontener z przepływem ustawionym tak, aby column wrapnie rozszerzał swojej szerokości podczas zawijania. Więcej informacji znajdziesz na code.google.com/p/chromium/issues/detail?id=247963 .
Gerrit Bertier
Nie mogę też zmusić go do pracy w IE11. Tam zawijane są najbardziej wewnętrzne elementy, ale wewnętrzna lista i jej pojemnik (element zewnętrzny) nie zwiększają swojej szerokości. Obecnie cofam się do przewijania list wewnętrznych, ale nie jest to dobre długoterminowe rozwiązanie i naprawdę chciałbym uniknąć dodawania do tego JavaScript.
Anders Tornblad

Odpowiedzi:

170

Problem

Wygląda to na fundamentalną wadę układu flex.

Pojemnik elastyczny w kierunku kolumn nie rozszerzy się, aby pomieścić dodatkowe kolumny. (To nie jest problem flex-direction: row.)

To pytanie było zadawane wiele razy (zobacz listę poniżej), bez wyraźnych odpowiedzi w CSS.

Trudno to uznać za błąd, ponieważ problem występuje we wszystkich głównych przeglądarkach. Ale rodzi pytanie:

Jak to możliwe, że wszystkie główne przeglądarki uzyskały rozszerzenie kontenera elastycznego podczas zawijania w kierunku wierszy, ale nie w kierunku kolumny?

Można by pomyśleć, że przynajmniej jeden z nich zrobi to dobrze. Mogę tylko spekulować na temat przyczyny. Może była to technicznie trudna implementacja i została odłożona na półkę na tę iterację.

AKTUALIZACJA: Problem wydaje się być rozwiązany w Edge v16.


Ilustracja problemu

OP stworzył przydatne demo ilustrujące problem. Kopiuję to tutaj: http://jsfiddle.net/nwccdwLw/1/


Opcje obejścia

Hacky rozwiązania społeczności Stack Overflow:


Więcej analiz


Inne posty opisujące ten sam problem

Michael Benjamin
źródło
2
Zgodnie z komentarzami raportu o błędzie, ten problem nie zostanie rozwiązany, dopóki nie wydadzą nowego silnika układu LayoutNG
Ben. 12
5
Liczba podanych przez Ciebie linków i podzielonych na kategorie jest naprawdę świetna. Chciałbym, żeby większość ludzi pisała odpowiedzi w ten sposób. Świetna odpowiedź.
Vignesh
4
Oto przydatne repozytorium zawierające listę kilku błędów Flexbox i ich obejść: Ten błąd jest wymieniony pod numerem 14
Ben.
czy możemy rozwiązać ten problem za pomocą siatki CSS?
Ismail Farooq
jeszcze jedna opcja obejścia: codepen.io/_mc2/pen/PoPmJRP Dla niektórych aspektów okazało się, że lepsze jest podejście do trybu zapisu
michalczerwinski
37

Spóźnił się na imprezę, ale LATA później napotkał ten problem. Skończyło się na znalezieniu rozwiązania za pomocą siatki. Na pojemniku możesz użyć

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

Mam przykład na CodePen, który przełącza się między problemem flexbox a poprawką siatki: https://codepen.io/MandeeD/pen/JVLdLd

MandeeD
źródło
1
To eleganckie obejście, jeśli kiedykolwiek je widziałem! Właśnie dostaję się do sieci i uwielbiam twoje rozwiązanie, dziękuję za to.
Albert
Na miejscu! Rozwiązałem mój problem.
Celestin Bochis
Nadal napotykam ten problem w przeglądarce Chrome. Miałem nadzieję, że uniknę obejścia siatki. Dobrze wiedzieć, że inni stosują to podejście!
trukvl
ratownik. Możesz również użyć, grid-row-end: span X;jeśli potrzebujesz niektórych elementów, aby zająć więcej wierszy.
Meirion Hughes
0

Szkoda, że ​​tak wiele głównych przeglądarek po wielu latach cierpi z powodu tego błędu. Rozważ obejście JavaScript. Za każdym razem, gdy okno przeglądarki zmieni rozmiar lub zawartość zostanie dodana do elementu, wykonaj ten kod, aby zmienić rozmiar na odpowiednią szerokość. Możesz zdefiniować dyrektywę w swoim frameworku, aby zrobić to za Ciebie.

    element.style.flexBasis = "auto";
    element.style.flexBasis = `${element.scrollWidth}px`;
Steve Hanov
źródło
0

Właśnie znalazłem tutaj naprawdę świetne obejście PURE CSS.

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

Trudność: ustaw writing-mode: vertical-lrw liście divwriting-mode: horizontal-tb w elemencie listy. Musiałem dostosować style w JSFiddle (usunąć wiele stylów wyrównania, które nie są konieczne do rozwiązania).

Uwaga: komentarz mówi, że działa tylko w przeglądarkach opartych na Chromium, a nie Firefox. Osobiście testowałem tylko w Chrome. Możliwe, że istnieje sposób, aby to zmodyfikować, aby działał w innych przeglądarkach, albo zostały zaktualizowane przeglądarki, które to umożliwiają.

Wielki okrzyk na ten komentarz: Kiedy elementy flexbox zawijają się w trybie kolumnowym, kontener nie zwiększa swojej szerokości . Przeglądanie tego wątku doprowadziło mnie do https://bugs.chromium.org/p/chromium/issues/detail?id=507397#c39, który doprowadził mnie do tego JSFiddle.

Dustin Kane
źródło
-1

Ponieważ nie zaproponowano jeszcze rozwiązania ani odpowiedniego obejścia, udało mi się uzyskać żądane zachowanie przy nieco innym podejściu. Zamiast rozdzielać układ na 3 różne elementy div, dodaję wszystkie elementy do jednego elementu div i utworzę separację z kilkoma elementami div pomiędzy nimi.

Proponowane rozwiązanie jest zakodowane na stałe, zakładając, że mamy 3 sekcje, ale można je rozszerzyć na ogólną. Głównym pomysłem jest wyjaśnienie, w jaki sposób możemy osiągnąć ten układ.

  1. Dodanie wszystkich elementów do 1 kontenera div, który używa flex do zawijania elementów
  2. Pierwszy element każdego „pojemnika wewnętrznego” (nazywam go sekcją) będzie miał klasę, która pomoże nam wykonać pewne manipulacje, które tworzą separację i stylizację każdej sekcji.
  3. Korzystając :beforez każdego pierwszego elementu, możemy zlokalizować tytuł każdej sekcji.
  4. Użycie spacetworzy lukę między sekcjami
  5. Ponieważ spacenie obejmuje całej wysokości sekcji, dodaję również :afterdo sekcji, ustawiając ją z pozycją bezwzględną i białym tłem.
  6. Aby nadać styl kolorowi tła każdej sekcji, dodaję kolejny element div w pierwszym elemencie każdej sekcji. Będę też stanowił stanowisko absolutne i będę miałz-index: -1 .
  7. Aby uzyskać odpowiednią szerokość każdego tła, używam JS, ustawiając odpowiednią szerokość, a także dodając odbiornik do zmiany rozmiaru.

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>

Itay Gal
źródło
-2

Możliwe rozwiązanie 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
źródło
To niestety nie pomogłoby, ponieważ wysokości poszczególnych elementów mogą się różnić. Tak więc liczba elementów nie jest interesująca ... Ale użycie columnswłaściwości style może być punktem wyjścia do praktycznego rozwiązania. Może dostosowując ilość kolumn i szerokość ul.
Anders Tornblad
1
Skończyło się również na rozwiązaniu tego problemu przez JS dla aplikacji React, nad którą pracowałem. Przeznaczony jest do pracy z elementami o dowolnej i zróżnicowanej wielkości. Przykład tutaj: djskinner.github.io/react-column-wrap . Kod źródłowy tutaj: github.com/djskinner/react-column-wrap
djskinner