Als flexbox-items in kolommodus worden gewikkeld, wordt de container niet breder

Ik werk aan een geneste flexbox-layout die als volgt zou moeten werken:

Het buitenste niveau (ul#main) is een horizontale lijst die naar rechts moet worden uitgevouwen als er meer items aan worden toegevoegd. Als het te groot wordt, zou er een horizontale schuifbalk moeten zijn.

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

Elk item van deze lijst (ul#main > li) heeft een koptekst (ul#main > li > h2) en een interne lijst (ul#main > li > ul.tasks). Deze binnenlijst is verticaal en moet indien nodig in kolommen worden gewikkeld. Bij het inpakken in meer kolommen, moet de breedte toenemen om ruimte te maken voor meer items. Deze vergroting van de breedte zou ook van toepassing moeten zijn op het bevattende item van de buitenste lijst.

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

Mijn probleem is dat de binnenste lijsten niet inlopen als de hoogte van het venster te klein wordt. Ik heb veel geprobeerd te knoeien met alle flex-eigenschappen, in een poging de richtlijnen op CSS-trucsminutieus, maar geen geluk.

Deze JSFiddlelaat zien wat ik tot nu toe heb.

Verwacht resultaat(wat ik wil):

Mijn gewenste uitvoer

Eigenlijk resultaat(wat ik krijg):

Mijn huidige output

Ouder resultaat(wat ik kreeg in 2015):

Mijn oudere uitvoer

UPDATE

Na enig onderzoek begint dit een groter probleem te worden. Alle belangrijke browsers gedragen zich op dezelfde manier, en het heeft niets te maken met het feit dat mijn flexbox-ontwerp genest is. Zelfs eenvoudigere flexbox-kolomlay-outs weigeren de breedte van de lijst te vergroten wanneer de items worden ingepakt.

Deze andere JSFiddlelaat duidelijk het probleem zien. In de huidige versies van Chrome, Firefox en IE11 worden alle items correct ingepakt; de hoogte van de lijst neemt toe in de modus row, maar de breedte neemt niet toe in de modus column. Er is ook geen onmiddellijke herschikking van elementen bij het wijzigen van de hoogte van een column-modus, maar wel in de row-modus.

Echter, de officiële specificaties (kijk specifiek bij voorbeeld 5)lijken aan te geven dat wat ik wil doen mogelijk moet zijn.

Kan iemand een oplossing voor dit probleem bedenken?

UPDATE 2

Na veel experimenteren met JavaScript om de hoogte en breedte van verschillende elementen bij te werken tijdens resize-evenementen, ben ik tot de conclusie gekomen dat het te complex en te veel moeite is om het op die manier op te lossen. Bovendien breekt het toevoegen van JavaScript absoluut het flexbox-model, dat zo schoon mogelijk moet worden gehouden.

Voorlopig val ik terug op overflow-y: autoin plaats van flex-wrap: wrapzodat de binnencontainer indien nodig verticaal schuift. Het is niet mooi, maar het is een weg vooruit die de bruikbaarheid in ieder geval niet te veel schaadt.


Antwoord 1, autoriteit 100%

Het probleem

Dit lijkt op een fundamentele tekortkoming in de flexlay-out.

Een flexcontainer in kolomrichting kan niet worden uitgevouwen om extra kolommen op te nemen. (Dit is geen probleem in flex-direction: row.)

Deze vraag is al vaak gesteld (zie onderstaande lijst), zonder duidelijke antwoorden in CSS.

Het is moeilijk om dit als een bug te bestempelen, omdat het probleem zich in alle belangrijke browsers voordoet. Maar het roept wel de vraag op:

Hoe is het mogelijk dat alle grote browsers de flexcontainer hebben gekregen?
uitvouwen bij omloop in rijrichting maar niet in kolomrichting?

Je zou denken dat minstens één van hen het goed zou doen. Over de reden kan ik alleen maar speculeren. Misschien was het een technisch moeilijke implementatie en werd het opgeschort voor deze iteratie.

UPDATE:het probleem lijkt te zijn opgelost in Edge v16.


Illustratie van het probleem

De OP heeft een handige demo gemaakt om het probleem te illustreren. Ik kopieer het hier: http://jsfiddle.net/nwccdwLw/1/


Opties voor tijdelijke oplossing

Hacky-oplossingen van de Stack Overflow-community:


Meer analyse


Andere berichten die hetzelfde probleem beschrijven


Antwoord 2, autoriteit 37%

Te laat op het feest, maar liep JAAR later nog steeds tegen dit probleem aan. Uiteindelijk een oplossing gevonden met behulp van raster. Op de container die je kunt gebruiken

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

Ik heb een voorbeeld op CodePen dat schakelt tussen het flexbox-probleem en de grid-fix: https://codepen .io/MandeeD/pen/JVLdLd


Antwoord 3, autoriteit 3%

Ik heb hier zojuist een geweldige PURE CSS-oplossing gevonden.

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

Het lastige: zet writing-mode: vertical-lrin de lijst div en vervolgens writing-mode: horizontal-tbin het lijstitem. Ik moest de stijlen in de JSFiddle aanpassen (veel van de uitlijningsstijlen verwijderen, die niet nodig zijn voor de oplossing).

Opmerking: in de opmerking staat dat het alleen werkt in op Chromium gebaseerde browsers, en niet in Firefox. Ik heb alleen persoonlijk getest in Chrome. Het is mogelijk dat er een manier is om dit aan te passen om het in andere browsers te laten werken, of er zijn updates voor deze browsers geweest waardoor dit werkt.

Grote shoutout naar deze opmerking: Als flexbox-items in kolommodus worden gewikkeld, wordt de container niet breder. Het doorspitten van die kwestie-thread leidde me naar https://bugs.chromium .org/p/chromium/issues/detail?id=507397#c39wat me naar deze JSFiddle leidde.


Antwoord 4, autoriteit 2%

Alleen CSS-oplossing

Bijna 6 jaar nadat deze vraag werd gesteld, bestaat deze flexbox-bug nog, dus hier is een CSS-only flex-direction: column-oplossing voor iedereen die uiteindelijk hier:

body {
  background-color: grey;
}
button {
  background-color: white;
  border: none;
  border-radius: 4px;
  width: 80px;
  height: 40px;
  margin: 4px;
}
/* WORKAROUND FOR flex-direction: column WITH WRAP IS BELOW */
.wrapped-columns {
  flex-direction: row;
  flex-wrap: wrap;
  writing-mode: vertical-lr;
  text-orientation: upright;
}
/* Ensures content is rendered correctly in Firefox */
.wrapped-columns * {
    writing-mode: horizontal-tb;
}
<div class="wrapped-columns">
  <button>Button 1</button>
  <button>Button 2</button>
  <button>Button 3</button>
  <button>Button 4</button>
  <button>Button 5</button>
  <button>Button 6</button>
  <button>Button 7</button>
  <button>Button 8</button>
  <button>Button 9</button>
  <button>Button 10</button>
  <button>Button 11</button>
  <button>Button 12</button>
  <button>Button 13</button>
  <button>Button 14</button>
</div>

Snippet uitvouwen


Antwoord 5

Het is jammer dat zoveel grote browsers na vele jaren last hebben van deze bug. Overweeg een Javascript-oplossing. Telkens wanneer het browservenster wordt vergroot of verkleind of inhoud aan het element wordt toegevoegd, voert u deze code uit om het formaat aan te passen aan de juiste breedte. U kunt een richtlijn in uw kader definiëren om het voor u te doen.

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

Antwoord 6

Omdat er nog geen oplossing of een goede tijdelijke oplossing werd voorgesteld, slaagde ik erin om het gevraagde gedrag te verkrijgen met een iets andere aanpak. In plaats van de lay-out op te delen in 3 verschillende div’s, voeg ik alle items toe aan 1 div en maak ik de scheiding met wat meer divs ertussen.

De voorgestelde oplossing is hard gecodeerd, ervan uitgaande dat we 3 secties hebben, maar kan worden uitgebreid tot een algemene. Het belangrijkste idee is om uit te leggen hoe we deze lay-out kunnen bereiken.

  1. Alle items toevoegen aan 1 container-div die flex gebruikt om de items in te pakken
  2. Het eerste item van elke “binnencontainer” (ik noem het een sectie) zal een klasse hebben, die ons helpt om enkele manipulaties uit te voeren die de scheiding en stijl van elke sectie creëren.
  3. Met :beforeop elk eerste item kunnen we de titel van elke sectie vinden.
  4. Het gebruik van spacecreëert de opening tussen de secties
  5. Omdat de spaceniet de volledige hoogte van de sectie beslaat, voeg ik ook :aftertoe aan de secties, zodat deze met een absolute positie en een witte achtergrond wordt gepositioneerd.
  6. Om de achtergrondkleur van elke sectie te stylen, voeg ik nog een div toe in het eerste item van elke sectie. Ik zal ook positie met absoluut zijn en zal z-index: -1hebben.
  7. Om de juiste breedte van elke achtergrond te krijgen, gebruik ik JS, stel ik de juiste breedte in en voeg ik ook een luisteraar toe om het formaat te wijzigen.
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>

Snippet uitvouwen


Antwoord 7

Ik heb mijn probleem op deze manier opgelost. Ik hoop dat het iemand anders helpt die op dit punt stuit:

 _ensureWidth(parentElement) {
let totalRealWidth = 0;
let parentElementBoundingRect  = parentElement.getBoundingClientRect()
let lowestLeft = parentElementBoundingRect.x;
let highestRight = parentElementBoundingRect.width;
for (let i = 0; i < parentElement.children.length; i++) {
  let { x, width } = parentElement.children[i].getBoundingClientRect();
  if (x < lowestLeft) {
    lowestLeft = x;
  }
  if (x + width > highestRight) {
    highestRight = x + width;
  }
}
totalRealWidth = highestRight - lowestLeft;
parentElement.style.width = `${totalRealWidth}px`;

}


Antwoord 8

Tussenoplossing:

met javascript is het niet moeilijk om de breedte van de wrapper handmatig in te stellen nadat elementen op het scherm zijn geladen. De breedte is altijd het rechterhandpunt van het laatste onderliggende element.

Als reactie daarop heb ik het bijgewerkt met de lay-outwijzigingen op basis van eventuele kinderen die aan de flex-wrapper worden toegevoegd, maar dit kan worden opgeroepen op elk moment dat u kinderen aan de wrapper toevoegt of verwijdert.

let r = refWrapper.current.lastElementChild.getBoundingClientRect()
refWrapper.current.style.width = (r.x+r.width )+'px'

`

waar refWrapper uw flex-element is


Antwoord 9

Mogelijke JS-oplossing..

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

Other episodes