Wenn Flexbox-Elemente im Spaltenmodus umbrochen werden, vergrößert der Container seine Breite nicht

166

Ich arbeite an einem verschachtelten Flexbox-Layout, das wie folgt funktionieren sollte:

Die äußerste Ebene ( ul#main) ist eine horizontale Liste, die nach rechts erweitert werden muss, wenn weitere Elemente hinzugefügt werden. Wenn es zu groß wird, sollte eine horizontale Bildlaufleiste vorhanden sein.

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

Jedes Element dieser Liste ( ul#main > li) hat einen Header ( ul#main > li > h2) und eine innere Liste ( ul#main > li > ul.tasks). Diese innere Liste ist vertikal und sollte bei Bedarf in Spalten eingeschlossen werden. Wenn Sie mehr Spalten einschließen, sollte sich die Breite erhöhen, um Platz für mehr Elemente zu schaffen. Diese Breitenerhöhung sollte auch für das enthaltene Element der äußeren Liste gelten.

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

Mein Problem ist, dass die inneren Listen nicht umbrochen werden, wenn die Höhe des Fensters zu klein wird. Ich habe viele Manipulationen an allen Flex-Eigenschaften versucht und versucht, die Richtlinien bei CSS-Tricks akribisch zu befolgen , aber kein Glück.

Diese JSFiddle zeigt, was ich bisher habe.

Erwartetes Ergebnis (was ich will) :

Meine gewünschte Ausgabe

Tatsächliches Ergebnis (was ich bekomme) :

Meine aktuelle Ausgabe

Älteres Ergebnis (was ich 2015 bekommen habe) :

Meine ältere Ausgabe

AKTUALISIEREN

Nach einigen Untersuchungen scheint dies ein größeres Problem zu sein. Alle gängigen Browser verhalten sich gleich und haben nichts damit zu tun, dass mein Flexbox-Design verschachtelt ist. Noch einfachere Flexbox-Spaltenlayouts weigern sich, die Listenbreite zu erhöhen, wenn die Elemente umbrochen werden.

Diese andere JSFiddle zeigt das Problem deutlich. In aktuellen Versionen von Chrome, Firefox und IE11 werden alle Elemente korrekt verpackt. Die Höhe der Liste nimmt im rowModus zu, die Breite im columnModus jedoch nicht . Es gibt auch überhaupt keinen sofortigen Rückfluss von Elementen, wenn die Höhe eines columnModus geändert wird, aber es gibt einen rowModus.

Die offiziellen Spezifikationen (siehe Beispiel 5) scheinen jedoch darauf hinzudeuten, dass das, was ich tun möchte, möglich sein sollte.

Kann jemand eine Problemumgehung für dieses Problem finden?

UPDATE 2

Nachdem ich viel mit JavaScript experimentiert habe, um die Höhe und Breite verschiedener Elemente während Größenänderungsereignissen zu aktualisieren, bin ich zu dem Schluss gekommen, dass es zu komplex und zu schwierig ist, es auf diese Weise zu lösen. Durch das Hinzufügen von JavaScript wird das Flexbox-Modell definitiv beschädigt, das so sauber wie möglich gehalten werden sollte.

Im Moment greife ich auf zurück, overflow-y: autoanstatt flex-wrap: wrapdass der innere Container bei Bedarf vertikal rollt. Es ist nicht schön, aber es ist ein Weg nach vorne, der zumindest die Benutzerfreundlichkeit nicht zu sehr beeinträchtigt.

Anders Tornblad
quelle
2
Nur eine Nebenbemerkung, aber es gibt einen Fehler in Chrome, der dazu führt, dass ein Container, dessen Flow so eingestellt ist, column wrapseine Breite beim Umbrechen nicht vergrößert. Weitere Informationen finden Sie unter code.google.com/p/chromium/issues/detail?id=247963 .
Gerrit Bertier
Ich kann es auch nicht in IE11 zum Laufen bringen. Dort werden die innersten Elemente umbrochen, aber die innere Liste und ihr Container (das äußere Element) vergrößern ihre Breite nicht. Momentan greife ich darauf zurück, dass die inneren Listen gescrollt werden, aber es ist keine gute langfristige Lösung, und ich möchte wirklich vermeiden, JavaScript dafür hinzuzufügen.
Anders Tornblad

Antworten:

170

Das Problem

Dies scheint ein grundlegender Mangel im Flex-Layout zu sein.

Ein Flex-Container in Spaltenrichtung wird nicht erweitert, um zusätzliche Spalten aufzunehmen. (Dies ist kein Problem in flex-direction: row.)

Diese Frage wurde oft gestellt (siehe Liste unten), ohne saubere Antworten in CSS.

Es ist schwierig, dies als Fehler zu erkennen, da das Problem in allen gängigen Browsern auftritt. Aber es wirft die Frage auf:

Wie ist es möglich, dass alle gängigen Browser den Flex-Container beim Zeilenumbruch in Zeilenrichtung, aber nicht in Spaltenrichtung erweitern konnten?

Sie würden denken, mindestens einer von ihnen würde es richtig machen. Ich kann nur über den Grund spekulieren. Vielleicht war es eine technisch schwierige Implementierung und wurde für diese Iteration zurückgestellt.

UPDATE: Das Problem scheint in Edge v16 behoben zu sein.


Illustration des Problems

Das OP hat eine nützliche Demo erstellt, die das Problem veranschaulicht. Ich kopiere es hier: http://jsfiddle.net/nwccdwLw/1/


Problemumgehungsoptionen

Hacky-Lösungen aus der Stack Overflow-Community:


Weitere Analyse


Andere Beiträge, die das gleiche Problem beschreiben

Michael Benjamin
quelle
2
Laut den Kommentaren des Fehlerberichts wird dieses Problem erst behoben, wenn sie ihre neue Layout-Engine LayoutNG
Ben.12
5
Die Anzahl der Links, die Sie bereitgestellt und kategorisiert haben, ist wirklich großartig. Ich wünschte, die meisten Leute schreiben Antworten auf diese Weise. Gute Antwort.
Vignesh
4
Hier ist ein nützliches Repo, in dem einige Flexbox-Fehler und
Problemumgehungen
Können wir dieses Problem mithilfe eines CSS-Rasters beheben?
Ismail Farooq
Eine weitere Problemumgehungsoption : codepen.io/_mc2/pen/PoPmJRP Für einige Aspekte fand ich es besser als den Schreibmodus-Ansatz
michalczerwinski
37

Spät zur Party, aber noch Jahre später auf diese Ausgabe gestoßen. Am Ende fand ich eine Lösung mit Grid. Auf dem Container können Sie verwenden

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

Ich habe ein Beispiel für CodePen, das zwischen dem Flexbox-Problem und dem Grid-Fix umschaltet: https://codepen.io/MandeeD/pen/JVLdLd

MandeeD
quelle
1
Das ist eine elegante Problemumgehung, wenn ich jemals eine gesehen habe! Ich steige gerade in die Startaufstellung ein und ich liebe Ihre Lösung. Vielen Dank dafür.
Albert
Genau richtig! Mein Problem wurde gelöst.
Celestin Bochis
In Chrome tritt dieses Problem immer noch auf. Ich hatte gehofft, die Verwendung einer Rasterumgehung zu vermeiden. Gut zu wissen, dass andere diesen Ansatz verwenden!
Trukvl
Lebensretter. Sie können auch verwenden, grid-row-end: span X;wenn Sie einige der Elemente benötigen, um mehr Zeilen aufzunehmen.
Meirion Hughes
0

Es ist bedauerlich, dass so viele große Browser nach vielen Jahren unter diesem Fehler leiden. Betrachten Sie eine Javascript-Problemumgehung. Wenn das Browserfenster die Größe ändert oder dem Element Inhalt hinzugefügt wird, führen Sie diesen Code aus, damit die Größe auf die richtige Breite geändert wird. Sie können in Ihrem Framework eine Direktive definieren, die dies für Sie erledigt.

    element.style.flexBasis = "auto";
    element.style.flexBasis = `${element.scrollWidth}px`;
Steve Hanov
quelle
-1

Da noch keine Lösung oder geeignete Problemumgehung vorgeschlagen wurde, gelang es mir, das angeforderte Verhalten mit einem etwas anderen Ansatz zu erhalten. Anstatt das Layout in 3 verschiedene Divs zu unterteilen, füge ich alle Elemente zu 1 Div hinzu und erstelle die Trennung mit einigen weiteren Divs dazwischen.

Die vorgeschlagene Lösung ist hartcodiert, vorausgesetzt, wir haben drei Abschnitte, kann aber auf einen generischen erweitert werden. Die Hauptidee ist zu erklären, wie wir dieses Layout erreichen können.

  1. Hinzufügen aller Elemente zu einem Container-Div, der Flex verwendet, um die Elemente zu verpacken
  2. Das erste Element jedes "inneren Containers" (ich nenne es einen Abschnitt) hat eine Klasse, die uns hilft, einige Manipulationen vorzunehmen, die die Trennung und das Styling jedes Abschnitts bewirken.
  3. Verwenden von :before jedes ersten Elements können wir den Titel jedes Abschnitts ermitteln.
  4. Mit spacewird die Lücke zwischen den Abschnitten erstellt
  5. Da der spaceAbschnitt nicht die gesamte Höhe des Abschnitts abdeckt, füge ich ihn auch :afterzu den Abschnitten hinzu , sodass er mit absoluter Position und weißem Hintergrund positioniert wird.
  6. Um die Hintergrundfarbe jedes Abschnitts zu gestalten, füge ich dem ersten Element jedes Abschnitts ein weiteres Div hinzu. Ich werde auch absolut positioniert sein und haben z-index: -1.
  7. Um die richtige Breite für jeden Hintergrund zu erhalten, verwende ich JS, stelle die richtige Breite ein und füge einen Listener hinzu, um die Größe zu ändern.

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
quelle
-2

Mögliche JS-Lösung ..

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
quelle
Das würde leider nicht helfen, da die Höhen der einzelnen Elemente variieren können. Die Anzahl der Elemente ist also nicht interessant ... Die Verwendung der columnsstyle-Eigenschaft könnte jedoch ein Ausgangspunkt für eine funktionierende Lösung sein. Vielleicht die Anzahl der Spalten und die Breite der Ul anpassen .
Anders Tornblad
1
Am Ende habe ich auch eine JS-Lösung für dieses Problem für eine React-Anwendung gefunden, an der ich gearbeitet habe. Es wurde entwickelt, um mit Elementen jeder und unterschiedlicher Größe zu arbeiten. Beispiel hier: djskinner.github.io/react-column-wrap . Quellcode hier: github.com/djskinner/react-column-wrap
djskinner