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):
Eigenlijk resultaat(wat ik krijg):
Ouder resultaat(wat ik kreeg in 2015):
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: auto
in plaats van flex-wrap: wrap
zodat 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
- Breedte flexbakcontainer groeit niet
- Hoe kan ik een display:flex container horizontaal laten uitzetten met de verpakte inhoud?
- Flex-flow: kolomomslag. Hoe de breedte van de container gelijk te stellen aan de inhoud?
- Flexbox flex-flow kolomomslag bugs in chroom?
- Hoe gebruik ik “flex-flow: column wrap”?
- Flexcontainer zet niet uit wanneer inhoud zich in een kolom wikkelt
- flex-flow: kolomomslag, in een flexbox waardoor de bovenliggende container overloopt
- Html flexbox-container zet niet uit over ingepakte kinderen
- Flexbox container en overvolle flex kinderen?
- Hoe kan ik een flexbox-container maken die uitrekt om in verpakte artikelen te passen?
- Flexcontainer die één kolom berekent, wanneer er meerdere kolommen zijn
- Maak container over de volledige breedte met flex
- Formaat van flexboxcontainer mogelijk?
- Flex-Wrap Inside Flex-Grow
- Flexbox groeien om te bevatten
- Flexbox-element uitbreiden tot de inhoud?
- flexbox-kolom uitrekken om in inhoud te passen
- https://stackoverflow.com/q/48406237/3597276
- flex-flow: kolomomloop rekt de breedte van het bovenliggende element niet uit
- Waarom werkt mijn <ul> vergroot de breedte om alle <li>?
- https://stackoverflow.com/q/55709208/3597276
- Flexbox-omslag vergroot de breedte van bovenliggende niet?
- Absolute Flex-container verandert niet naar de juiste breedte met gedefinieerde maximale hoogte
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-lr
in de lijst div en vervolgens writing-mode: horizontal-tb
in 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>
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.
- Alle items toevoegen aan 1 container-div die flex gebruikt om de items in te pakken
- 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.
- Met
:before
op elk eerste item kunnen we de titel van elke sectie vinden. - Het gebruik van
space
creëert de opening tussen de secties - Omdat de
space
niet de volledige hoogte van de sectie beslaat, voeg ik ook:after
toe aan de secties, zodat deze met een absolute positie en een witte achtergrond wordt gepositioneerd. - 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: -1
hebben. - 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>
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");
}
}