Quando gli oggetti flexbox si avvolgono in modalità colonna, il contenitore non aumenta di larghezza


168

Sto lavorando su un layout nidificato di flexbox che dovrebbe funzionare come segue:

Il livello più esterno ( ul#main) è un elenco orizzontale che deve espandersi a destra quando vengono aggiunti più elementi. Se diventa troppo grande, dovrebbe esserci una barra di scorrimento orizzontale.

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

Ogni elemento di questo elenco ( ul#main > li) ha un'intestazione ( ul#main > li > h2) e un elenco interno ( ul#main > li > ul.tasks). Questo elenco interno è verticale e dovrebbe essere suddiviso in colonne quando necessario. Quando si avvolge in più colonne, la sua larghezza dovrebbe aumentare per fare spazio a più elementi. Questo aumento di larghezza dovrebbe applicarsi anche all'elemento contenente dell'elenco esterno.

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

Il mio problema è che gli elenchi interni non si chiudono quando l'altezza della finestra diventa troppo piccola. Ho provato a manomettere molte proprietà flex, cercando di seguire meticolosamente le linee guida di CSS-Tricks , ma senza fortuna.

Questo JSFiddle mostra ciò che ho finora.

Risultato atteso (quello che voglio) :

La mia uscita desiderata

Risultato effettivo (quello che ottengo) :

La mia uscita attuale

Risultato precedente (quello che ho ottenuto nel 2015) :

La mia uscita più vecchia

AGGIORNARE

Dopo alcune indagini, questo inizia a sembrare un problema più grande. Tutti i principali browser si comportano allo stesso modo e non ha nulla a che fare con il design del mio flexbox nidificato. Anche i layout di colonna di flexbox più semplici rifiutano di aumentare la larghezza dell'elenco quando gli articoli vengono spostati.

Quest'altro JSFiddle dimostra chiaramente il problema. Nelle attuali versioni di Chrome, Firefox e IE11, tutti gli elementi si avvolgono correttamente; l'altezza dell'elenco aumenta in rowmodalità, ma la sua larghezza non aumenta in columnmodalità. Inoltre, non c'è alcun riflusso immediato degli elementi quando si cambia l'altezza di una columnmodalità, ma è presente nella rowmodalità.

Tuttavia, le specifiche ufficiali (guarda in particolare l'esempio 5) sembrano indicare che ciò che voglio fare dovrebbe essere possibile.

Qualcuno può trovare una soluzione a questo problema?

AGGIORNAMENTO 2

Dopo molti esperimenti con JavaScript per aggiornare l'altezza e la larghezza di vari elementi durante gli eventi di ridimensionamento, sono giunto alla conclusione che è troppo complesso e troppi problemi per provare a risolverlo in quel modo. Inoltre, l'aggiunta di JavaScript interrompe sicuramente il modello di flexbox, che dovrebbe essere mantenuto il più pulito possibile.

Per ora, sto ricadendo overflow-y: autoinvece flex-wrap: wrapche in modo che il contenitore interno scorra verticalmente quando necessario. Non è carino, ma è un modo in avanti che almeno non rompe troppo l'usabilità.


2
Solo un sidenote, ma c'è un bug in Chrome che causa un contenitore con il suo flusso impostato su column wrapnon espandere la sua larghezza durante il wrapping. Consulta code.google.com/p/chromium/issues/detail?id=247963 per ulteriori informazioni.
Gerrit Bertier,

Non riesco nemmeno a farlo funzionare in IE11. Lì, gli oggetti più interni si avvolgono, ma l'elenco interno e il suo contenitore (l'elemento esterno) non aumentano la loro larghezza. Attualmente sto ricadendo sull'avanzamento degli elenchi interni, ma non è una buona soluzione a lungo termine e vorrei davvero evitare di aggiungere JavaScript per questo.
Anders Tornblad,

Risposte:


171

Il problema

Sembra una carenza fondamentale nel layout flessibile.

Un contenitore flessibile nella direzione della colonna non si espanderà per ospitare colonne aggiuntive. (Questo non è un problema flex-direction: row.)

Questa domanda è stata posta molte volte (vedere l'elenco di seguito), senza risposte chiare nei CSS.

È difficile identificarlo come un bug perché il problema si verifica in tutti i principali browser. Ma solleva la domanda:

Come è possibile che tutti i principali browser abbiano il contenitore flessibile espandersi su avvolgimento nella direzione della riga ma non nella direzione della colonna?

Penseresti che almeno uno di loro avrebbe capito bene. Posso solo speculare sul motivo. Forse è stata un'implementazione tecnicamente difficile ed è stata accantonata per questa iterazione.

AGGIORNAMENTO: il problema sembra essere stato risolto in Edge v16.


Illustrazione del problema

L'OP ha creato un'utile demo che illustra il problema. Lo sto copiando qui: http://jsfiddle.net/nwccdwLw/1/


Opzioni alternative

Soluzioni Hacky della community Stack Overflow:


Più analisi


Altri post che descrivono lo stesso problema


2
Secondo i commenti sulla segnalazione di bug, questo problema non verrà risolto fino a quando non rilasceranno il loro nuovo motore di layout LayoutNG
Ben.12

5
Il numero di collegamenti forniti e classificati è davvero eccezionale. Vorrei che molte persone scrivessero risposte in questo modo. Bella risposta.
Vignesh

4
Ecco un utile repository che elenca diversi bug e soluzioni alternative per Flexbox: Questo bug è elencato al n. 14
Ben.12

possiamo risolvere questo problema usando una griglia CSS?
Ismail Farooq,

un'altra soluzione alternativa: codepen.io/_mc2/pen/PoPmJRP Per qualche aspetto ho trovato meglio questo approccio in modalità di scrittura
michalczerwinski

38

In ritardo alla festa, ma stavo ancora incontrando questo problema ANNI dopo. Ho finito per trovare una soluzione usando la griglia. Sul contenitore è possibile utilizzare

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

Ho un esempio su CodePen che alterna il problema del flexbox e la correzione della griglia: https://codepen.io/MandeeD/pen/JVLdLd


1
È una soluzione elegante se mai ne ho visto uno! Sto entrando nella griglia e adoro la tua soluzione, grazie per questo.
Albert,

Spot on! Risolto il mio problema
Celestin Bochis,

Riscontro ancora questo problema in Chrome. Speravo di evitare di usare una soluzione alternativa alla griglia. Buono a sapersi, altri stanno usando questo approccio!
trukvl,

salvavita. Puoi anche utilizzare grid-row-end: span X;se hai bisogno di alcuni degli elementi per occupare più righe.
Meirion Hughes,

0

È un peccato che così tanti browser importanti soffrano di questo errore dopo molti anni. Prendi in considerazione una soluzione alternativa a Javascript. Ogni volta che la finestra del browser viene ridimensionata o viene aggiunto del contenuto all'elemento, eseguire questo codice per farlo ridimensionare alla larghezza corretta. Puoi definire una direttiva nel tuo framework per farlo per te.

    element.style.flexBasis = "auto";
    element.style.flexBasis = `${element.scrollWidth}px`;

0

Ho appena trovato una soluzione PURE CSS davvero fantastica qui.

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

Il difficile: impostare writing-mode: vertical-lrnella div elenco quindi writing-mode: horizontal-tbnella voce di elenco. Ho dovuto modificare gli stili in JSFiddle (rimuovere molti stili di allineamento, che non sono necessari per la soluzione).

Nota: il commento dice che funziona solo nei browser basati su Chromium e non su Firefox. Ho testato personalmente solo in Chrome. È possibile o c'è un modo per modificarlo per farlo funzionare in altri browser o ci sono stati aggiornamenti a detti browser che lo fanno funzionare.

Grande richiamo a questo commento: quando gli oggetti flexbox si avvolgono in modalità colonna, il contenitore non aumenta di larghezza . La ricerca di questo thread di discussione mi ha portato a https://bugs.chromium.org/p/chromium/issues/detail?id=507397#c39 che mi ha portato a questo JSFiddle.


-1

Poiché non è stata ancora suggerita alcuna soluzione o soluzione alternativa, sono riuscito a ottenere il comportamento richiesto con un approccio leggermente diverso. Invece di separare il layout in 3 div diversi, sto aggiungendo tutti gli elementi in 1 div e creando la separazione con alcuni div in mezzo.

La soluzione proposta è hard coded, supponendo che abbiamo 3 sezioni, ma può essere estesa a una generica. L'idea principale è di spiegare come possiamo ottenere questo layout.

  1. Aggiunta di tutti gli articoli in 1 div contenitore che utilizza flex per avvolgere gli articoli
  2. Il primo elemento di ogni "contenitore interno" (lo chiamerò una sezione) avrà una classe, che ci aiuta a fare alcune manipolazioni che creano la separazione e lo stile di ogni sezione.
  3. Usando :beforesu ogni primo elemento, possiamo individuare il titolo di ogni sezione.
  4. L'uso spacecrea il divario tra le sezioni
  5. Dal momento spaceche non coprirà l'intera altezza della sezione, sto aggiungendo anche :afterle sezioni, posizionandola con posizione assoluta e sfondo bianco.
  6. Per dare uno stile al colore di sfondo di ogni sezione, sto aggiungendo un altro div all'interno del primo elemento di ogni sezione. Sarò anche io in posizione assoluta e avrò z-index: -1.
  7. Per ottenere la larghezza corretta di ogni sfondo, sto usando JS, impostando la larghezza corretta e aggiungendo anche un ascoltatore per ridimensionare.

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

Possibile soluzione 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");
     }
}

Ciò non aiuterebbe, sfortunatamente, poiché le altezze dei singoli elementi possono variare. Quindi il numero di elementi non è interessante ... Ma l'uso della columnsproprietà style potrebbe essere un punto di partenza per una soluzione funzionante. Magari regolando il numero di colonne e la larghezza dell'ul.
Anders Tornblad,

1
Ho anche finito con una soluzione JS a questo problema per un'applicazione React a cui stavo lavorando. È progettato per funzionare con elementi di qualsiasi dimensione. Esempio qui: djskinner.github.io/react-column-wrap . Codice sorgente qui: github.com/djskinner/react-column-wrap
djskinner
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.