Quando os itens do flexbox são agrupados no modo de coluna, o contêiner não aumenta sua largura


167

Estou trabalhando em um layout aninhado de flexbox que deve funcionar da seguinte maneira:

O nível mais externo ( ul#main) é uma lista horizontal que deve ser expandida para a direita quando mais itens forem adicionados a ela. Se crescer muito, deve haver uma barra de rolagem horizontal.

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

Cada item desta lista ( ul#main > li) possui um cabeçalho ( ul#main > li > h2) e uma lista interna ( ul#main > li > ul.tasks). Essa lista interna é vertical e deve quebrar em colunas quando necessário. Ao agrupar em mais colunas, sua largura deve aumentar para abrir espaço para mais itens. Esse aumento de largura deve ser aplicado também ao item que contém a lista externa.

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

Meu problema é que as listas internas não quebram quando a altura da janela fica muito pequena. Eu tentei muitas adulterações com todas as propriedades flex, tentando seguir as diretrizes do CSS-Tricks meticulosamente, mas sem sorte.

Este JSFiddle mostra o que tenho até agora.

Resultado esperado (o que eu quero) :

Minha saída desejada

Resultado real (o que eu recebo) :

Minha saída atual

Resultado mais antigo (o que obtive em 2015) :

Minha saída mais antiga

ATUALIZAR

Após alguma investigação, isso está começando a parecer um problema maior. Todos os principais navegadores se comportam da mesma maneira e não tem nada a ver com o design do meu flexbox sendo aninhado. Layouts de colunas flexbox ainda mais simples se recusam a aumentar a largura da lista quando os itens são quebrados.

Este outro JSFiddle demonstra claramente o problema. Nas versões atuais do Chrome, Firefox e IE11, todos os itens são agrupados corretamente; a altura da lista aumenta no rowmodo, mas sua largura não aumenta no columnmodo. Além disso, não há refluxo imediato de elementos ao alterar a altura de um columnmodo, mas existe no rowmodo.

No entanto, as especificações oficiais (veja especificamente o exemplo 5) parecem indicar que o que eu quero fazer deve ser possível.

Alguém pode apresentar uma solução alternativa para esse problema?

ATUALIZAÇÃO 2

Depois de muitas experiências usando o JavaScript para atualizar a altura e a largura de vários elementos durante eventos de redimensionamento, cheguei à conclusão de que é muito complexo e complicado demais tentar resolvê-lo dessa maneira. Além disso, adicionar JavaScript definitivamente quebra o modelo do flexbox, que deve ser mantido o mais limpo possível.

Por enquanto, estou retornando ao overflow-y: autoinvés de flex-wrap: wrappara que o contêiner interno role verticalmente quando necessário. Não é bonito, mas é um caminho a seguir que pelo menos não quebra muito a usabilidade.


2
Apenas uma nota de rodapé, mas há um erro no Chrome que faz com que um contêiner com seu fluxo definido column wrapnão exiba sua largura ao quebrar. Consulte code.google.com/p/chromium/issues/detail?id=247963 para obter mais informações.
Gerrit Bertier

Também não consigo fazê-lo funcionar no IE11. Lá, os itens mais internos são quebrados, mas a lista interna e seu contêiner (o item externo) não aumentam sua largura. Atualmente, estou voltando ao rolagem das listas internas, mas não é uma boa solução a longo prazo, e eu realmente gostaria de evitar adicionar JavaScript para isso.
Anders Tornblad

Respostas:


170

O problema

Isso parece uma deficiência fundamental no layout flexível.

Um contêiner flexível na direção da coluna não será expandido para acomodar colunas adicionais. (Este não é um problema no flex-direction: row.)

Esta pergunta foi feita muitas vezes (veja a lista abaixo), sem respostas limpas em CSS.

É difícil definir isso como um bug, porque o problema ocorre em todos os principais navegadores. Mas levanta a questão:

Como é possível que todos os principais navegadores tenham o contêiner flex expandido na quebra automática na direção da linha, mas não na direção da coluna?

Você pensaria que pelo menos um deles acertaria. Só posso especular sobre o motivo. Talvez tenha sido uma implementação tecnicamente difícil e tenha sido arquivada para essa iteração.

ATUALIZAÇÃO: o problema parece ter sido resolvido no Edge v16.


Ilustração do Problema

O OP criou uma demonstração útil ilustrando o problema. Estou copiando aqui: http://jsfiddle.net/nwccdwLw/1/


Opções de solução alternativa

Soluções Hacky da comunidade Stack Overflow:


Mais análise


Outras postagens descrevendo o mesmo problema


2
De acordo com os comentários do relatório de bug, este problema não será corrigido até que eles lançar seu novo mecanismo de layout LayoutNG
Ben.12

5
O número de links que você forneceu e os categorizou é realmente grande. Desejo que a maioria das pessoas escreva respostas dessa maneira. Ótima resposta.
Vignesh

4
Aqui está um repositório útil listando vários bugs do Flexbox e soluções alternativas para eles: Esse bug está listado em # 14
Ben

podemos corrigir esse problema usando uma grade CSS?
Ismail Farooq

mais uma opção alternativa: codepen.io/_mc2/pen/PoPmJRP Por algum aspecto, achei melhor essa abordagem no modo de gravação
michalczerwinski 26/04

37

Chegou atrasado à festa, mas ainda estava enfrentando esse problema ANOS depois. Acabou encontrando uma solução usando a grade. No contêiner, você pode usar

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

Eu tenho um exemplo no CodePen que alterna entre o problema do flexbox e a correção da grade: https://codepen.io/MandeeD/pen/JVLdLd


1
Essa é uma solução elegante, se eu já vi uma! Estou entrando no grid e adoro a sua solução, obrigado por isso.
Albert

Spot on! Resolvido meu problema.
Celestin Bochis 31/10/19

Ainda está com esse problema no Chrome. Eu esperava evitar usar uma solução alternativa da grade. É bom saber que outras pessoas estão usando essa abordagem!
trukvl

salva-vidas. Você também pode usar grid-row-end: span X;se precisar de alguns itens para ocupar mais linhas.
Meirion Hughes

0

É lamentável que tantos navegadores importantes sofram desse bug após muitos anos. Considere uma solução alternativa para Javascript. Sempre que a janela do navegador é redimensionada ou o conteúdo é adicionado ao elemento, execute esse código para redimensionar a largura adequada. Você pode definir uma diretiva em sua estrutura para fazer isso por você.

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

0

Acabei de encontrar uma solução PURE CSS realmente incrível aqui.

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

O mais complicado: defina writing-mode: vertical-lrna lista div e depois writing-mode: horizontal-tbno item da lista. Eu tive que ajustar os estilos no JSFiddle (remova muitos estilos de alinhamento, que não são necessários para a solução).

Nota: o comentário diz que só funciona em navegadores baseados no Chromium, e não no Firefox. Eu só testei pessoalmente no Chrome. É possível que haja uma maneira de modificar isso para fazê-lo funcionar em outros navegadores ou se houve atualizações nos referidos navegadores que fazem isso funcionar.

Grande mensagem para este comentário: quando os itens do flexbox são agrupados no modo de coluna, o contêiner não aumenta sua largura . A pesquisa nesse tópico levou-me a https://bugs.chromium.org/p/chromium/issues/detail?id=507397#c39, o que me levou a este JSFiddle.


-1

Como ainda não foi sugerida nenhuma solução ou solução alternativa, consegui obter o comportamento solicitado com uma abordagem um pouco diferente. Em vez de separar o layout em 3 divs diferentes, estou adicionando todos os itens em 1 div e criando a separação com mais divs no meio.

A solução proposta é codificada, assumindo que temos três seções, mas pode ser estendida para uma genérica. A idéia principal é explicar como podemos alcançar esse layout.

  1. Adicionando todos os itens em 1 div de contêiner que usa flex para agrupar os itens
  2. O primeiro item de cada "contêiner interno" (vou chamá-lo de seção) terá uma classe, o que nos ajuda a fazer algumas manipulações que criam a separação e o estilo de cada seção.
  3. Usando :beforeem cada primeiro item, podemos localizar o título de cada seção.
  4. Usando space cria a lacuna entre as seções
  5. Como o spacenão cobrirá toda a altura da seção, também adicionarei:after às seções, posicionando-a com posição absoluta e fundo branco.
  6. Para estilizar a cor de fundo de cada seção, estou adicionando outra div dentro do primeiro item de cada seção. Estarei em posição absoluta também e terei z-index: -1.
  7. Para obter a largura correta de cada plano de fundo, estou usando JS, definindo a largura correta e também adicionando um ouvinte para redimensionar.

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

Possível solução 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");
     }
}

Infelizmente, isso não ajudaria, pois as alturas dos elementos individuais podem variar. Portanto, o número de elementos não é interessante ... Mas o uso da columnspropriedade style pode ser o ponto de partida para uma solução funcional. Talvez ajustando o número de colunas e a largura da ul.
Anders Tornblad

1
Também acabei indo com uma solução JS para esse problema para um aplicativo React em que estou trabalhando. Ele foi projetado para trabalhar com elementos de qualquer tamanho e tamanho variável. Exemplo aqui: djskinner.github.io/react-column-wrap . Código-fonte aqui: github.com/djskinner/react-column-wrap
djskinner 12/17
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.