Cuando los elementos flexbox se envuelven en modo columna, el contenedor no aumenta su ancho

167

Estoy trabajando en un diseño de caja flexible anidado que debería funcionar de la siguiente manera:

El nivel más externo ( ul#main) es una lista horizontal que debe expandirse a la derecha cuando se le agregan más elementos. Si crece demasiado, debe haber una barra de desplazamiento horizontal.

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

Cada elemento de esta lista ( ul#main > li) tiene un encabezado ( ul#main > li > h2) y una lista interna ( ul#main > li > ul.tasks). Esta lista interna es vertical y debe ajustarse en columnas cuando sea necesario. Al envolver en más columnas, su ancho debe aumentar para dejar espacio para más artículos. Este aumento de ancho debería aplicarse también al elemento contenedor de la lista externa.

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

Mi problema es que las listas internas no se ajustan cuando la altura de la ventana es demasiado pequeña. He intentado muchas manipulaciones con todas las propiedades de flex, tratando de seguir las pautas de CSS-Tricks meticulosamente, pero no tuve suerte.

Este JSFiddle muestra lo que tengo hasta ahora.

Resultado esperado (lo que quiero) :

Mi salida deseada

Resultado real (lo que obtengo) :

Mi salida actual

Resultado anterior (lo que obtuve en 2015) :

Mi salida anterior

ACTUALIZAR

Después de algunas investigaciones, esto comienza a parecer un problema mayor. Todos los principales navegadores se comportan de la misma manera , y no tiene nada que ver con que mi diseño de Flexbox esté anidado. Diseños de columna de flexbox aún más simples se niegan a aumentar el ancho de la lista cuando los elementos se ajustan.

Este otro JSFiddle demuestra claramente el problema. En las versiones actuales de Chrome, Firefox e IE11, todos los elementos se ajustan correctamente; la altura de la lista aumenta en rowmodo, pero su ancho no aumenta en columnmodo. Además, no hay reflujo inmediato de elementos cuando se cambia la altura de un columnmodo, pero sí está en rowmodo.

Sin embargo, las especificaciones oficiales (mira específicamente el ejemplo 5) parecen indicar que lo que quiero hacer debería ser posible.

¿Alguien puede encontrar una solución a este problema?

ACTUALIZACIÓN 2

Después de mucho experimentar usando JavaScript para actualizar la altura y el ancho de varios elementos durante los eventos de cambio de tamaño, he llegado a la conclusión de que es demasiado complejo y demasiados problemas tratar de resolverlo de esa manera. Además, agregar JavaScript definitivamente rompe el modelo de flexbox, que debe mantenerse lo más limpio posible.

Por ahora, me estoy volviendo hacia atrás en overflow-y: autolugar de flex-wrap: wrapque el contenedor interno se desplace verticalmente cuando sea necesario. No es bonito, pero es un camino a seguir que al menos no rompe demasiado la usabilidad.

Anders Tornblad
fuente
2
Solo una nota al margen, pero hay un error en Chrome que hace que un contenedor con su flujo establecido column wrapno expanda su ancho cuando se ajusta. Consulte code.google.com/p/chromium/issues/detail?id=247963 para obtener más información.
Gerrit Bertier
Tampoco puedo hacer que funcione en IE11. Allí, los elementos más internos se envuelven, pero la lista interna y su contenedor (el elemento externo) no aumentan su ancho. Actualmente estoy volviendo a tener el desplazamiento de las listas internas, pero no es una buena solución a largo plazo, y realmente me gustaría evitar agregar JavaScript para esto.
Anders Tornblad

Respuestas:

170

El problema

Esto parece una deficiencia fundamental en el diseño flexible.

Un contenedor flexible en la dirección de la columna no se expandirá para acomodar columnas adicionales. (Esto no es un problema en flex-direction: row)

Esta pregunta se ha hecho muchas veces (ver la lista a continuación), sin respuestas claras en CSS.

Es difícil identificar esto como un error porque el problema ocurre en todos los principales navegadores. Pero plantea la pregunta:

¿Cómo es posible que todos los principales navegadores obtuvieran el contenedor flexible para expandirse en la envoltura en dirección de fila pero no en dirección de columna?

Uno pensaría que al menos uno de ellos lo entendería bien. Solo puedo especular sobre la razón. Tal vez fue una implementación técnicamente difícil y se archivó para esta iteración.

ACTUALIZACIÓN: El problema parece resolverse en Edge v16.


Ilustración del problema

El OP creó una demostración útil que ilustra el problema. Lo estoy copiando aquí: http://jsfiddle.net/nwccdwLw/1/


Opciones de solución

Soluciones de Hacky de la comunidad Stack Overflow:


Más análisis


Otras publicaciones que describen el mismo problema

Michael Benjamin
fuente
2
De acuerdo con los comentarios del informe de error, este problema no se solucionará hasta que lancen su nuevo motor de diseño LayoutNG
Ben.12
55
La cantidad de enlaces que proporcionó y los clasificó es realmente excelente. Deseo que la mayoría de las personas escriban respuestas de esta manera. Gran respuesta.
Vignesh
44
Aquí hay un repositorio útil que enumera varios errores de Flexbox y soluciones alternativas para ellos: Este error aparece en el n. ° 14
Ben.12
¿podemos solucionar este problema usando una cuadrícula CSS?
Ismail Farooq
una opción más de solución alternativa: codepen.io/_mc2/pen/PoPmJRP Por algún aspecto, encontré mejor ese enfoque de modo de escritura
michalczerwinski
37

Llegó tarde a la fiesta, pero todavía se encontraba con este problema AÑOS más tarde. Terminé buscando una solución usando la cuadrícula. En el contenedor puedes usar

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

Tengo un ejemplo en CodePen que alterna entre el problema de flexbox y la corrección de la cuadrícula: https://codepen.io/MandeeD/pen/JVLdLd

MandeeD
fuente
1
¡Esa es una solución elegante si alguna vez he visto una! Estoy entrando en la red y me encanta tu solución, gracias por esto.
Albert
¡Correcto! Resuelto mi problema
Celestin Bochis
Todavía me encuentro con este problema en Chrome. Tenía la esperanza de evitar el uso de una solución de red. ¡Es bueno saber que otros están usando este enfoque!
trukvl
salvador de la vida. También puede usarlo grid-row-end: span X;si necesita algunos de los elementos para ocupar más filas.
Meirion Hughes
0

Es lamentable que tantos navegadores importantes sufran este error después de muchos años. Considere una solución alternativa de Javascript. Siempre que la ventana del navegador cambie de tamaño, o se agregue contenido al elemento, ejecute este código para que cambie el tamaño al ancho adecuado. Puede definir una directiva en su marco para hacerlo por usted.

    element.style.flexBasis = "auto";
    element.style.flexBasis = `${element.scrollWidth}px`;
Steve Hanov
fuente
0

Acabo de encontrar una solución CSS PURE realmente increíble aquí.

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

El complicado: establecer writing-mode: vertical-lren la lista div luego writing-mode: horizontal-tben el elemento de la lista. Tuve que ajustar los estilos en JSFiddle (eliminar muchos de los estilos de alineación, que no son necesarios para la solución).

Nota: el comentario dice que solo funciona en navegadores basados ​​en Chromium, y no en Firefox. Solo lo he probado personalmente en Chrome. Es posible que haya una forma de modificar esto para que funcione en otros navegadores o que haya actualizaciones en dichos navegadores que hagan que esto funcione.

Gran agradecimiento a este comentario: cuando los elementos de flexbox se ajustan en modo columna, el contenedor no aumenta su ancho . Excavar a través de ese tema me llevó a https://bugs.chromium.org/p/chromium/issues/detail?id=507397#c39 que me llevó a este JSFiddle.

Dustin Kane
fuente
-1

Como todavía no se sugirió ninguna solución o solución adecuada, logré obtener el comportamiento solicitado con un enfoque un poco diferente. En lugar de separar el diseño en 3 divs diferentes, estoy agregando todos los elementos en 1 div y creando la separación con algunos divs más en el medio.

La solución propuesta está codificada, suponiendo que tengamos 3 secciones, pero puede extenderse a una genérica. La idea principal es explicar cómo podemos lograr este diseño.

  1. Agregar todos los artículos en 1 contenedor div que usa flex para envolver los artículos
  2. El primer elemento de cada "contenedor interno" (lo llamaré una sección) tendrá una clase, que nos ayuda a hacer algunas manipulaciones que crean la separación y el estilo de cada sección.
  3. Usando :beforecada primer elemento, podemos ubicar el título de cada sección.
  4. El uso spacecrea el espacio entre las secciones
  5. Como spaceno cubrirá toda la altura de la sección, también agregaré:after a las secciones, de modo que la coloque con posición absoluta y fondo blanco.
  6. Para diseñar el color de fondo de cada sección, agrego otro div dentro del primer elemento de cada sección. Estaré en posición absoluta también y tendréz-index: -1 .
  7. Para obtener el ancho correcto de cada fondo, estoy usando JS, establezco el ancho correcto y también agrego un oyente para cambiar el tamaño.

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

Posible solución 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");
     }
}
usuario2323336
fuente
Desafortunadamente, eso no ayudaría, ya que las alturas de los elementos individuales pueden variar. Por lo tanto, el número de elementos no es interesante ... Pero usar la columnspropiedad de estilo podría ser un punto de partida para una solución de trabajo. Tal vez ajustando el número de columnas y el ancho de la ul.
Anders Tornblad
1
También terminé yendo con una solución JS a este problema para una aplicación React en la que he estado trabajando. Está diseñado para trabajar con elementos de cualquier tamaño y variados. Ejemplo aquí: djskinner.github.io/react-column-wrap . Código fuente aquí: github.com/djskinner/react-column-wrap
djskinner