Mantenga el elemento central centrado cuando los elementos laterales tengan anchos diferentes

162

ingrese la descripción de la imagen aquí

Imagine el siguiente diseño, donde los puntos representan el espacio entre los cuadros:

[Left box]......[Center box]......[Right box]

Cuando elimino el cuadro derecho, me gusta que el cuadro central todavía esté en el centro, así:

[Left box]......[Center box].................

Lo mismo ocurre si quitara el cuadro de la izquierda.

................[Center box].................

Ahora, cuando el contenido dentro del recuadro central se alargue, ocupará tanto espacio disponible como sea necesario mientras permanece centrado. El cuadro de la izquierda y la derecha no se encogerá y por lo tanto cuando no hay espacio en el que se dejó el overflow:hiddeny text-overflow: ellipsisvendrá en efecto para romper el contenido;

[Left box][Center boxxxxxxxxxxxxx][Right box]

Todo lo anterior es mi situación ideal, pero no tengo idea de cómo lograr este efecto. Porque cuando creo una estructura flexible así:

.parent {
    display : flex; // flex box
    justify-content : space-between; // horizontal alignment
    align-content   : center; // vertical alignment
}

Si los cuadros izquierdo y derecho fueran exactamente del mismo tamaño, obtendré el efecto deseado. Sin embargo, cuando uno de los dos es de un tamaño diferente, el cuadro centrado ya no está realmente centrado.

¿Hay alguien que pueda ayudarme?

Actualizar

A justify-selfsería bueno, esto sería ideal:

.leftBox {
     justify-self : flex-start;
}

.rightBox {
    justify-self : flex-end;
}
marca
fuente
3
Básicamente ... no puedes. Así no flexboxse supone que funcione. Puedes probar otra metodología.
Paulie_D
1
Realmente completaría flexbox si lo hiciera. Porque flexbox se trata de distribuir espacio y cómo se comportan los elementos dentro.
Mark
1
Discutimos más justify-selftemprano hoy, por lo que puede encontrar esto interesante: stackoverflow.com/questions/32551291/…
Michael Benjamin

Respuestas:

130

Si los cuadros izquierdo y derecho fueran exactamente del mismo tamaño, obtendré el efecto deseado. Sin embargo, cuando uno de los dos tiene un tamaño diferente, el cuadro centrado ya no está realmente centrado. ¿Hay alguien que pueda ayudarme?

Aquí hay un método que usa flexbox para centrar el elemento central, independientemente del ancho de los hermanos.

Características clave:

  • CSS puro
  • sin posicionamiento absoluto
  • sin JS / jQuery

Utilice contenedores flexibles y automárgenes anidados :

.container {
  display: flex;
}
.box {
  flex: 1;
  display: flex;
  justify-content: center;
}

.box:first-child > span { margin-right: auto; }

.box:last-child  > span { margin-left: auto;  }

/* non-essential */
.box {
  align-items: center;
  border: 1px solid #ccc;
  background-color: lightgreen;
  height: 40px;
}
p {
  text-align: center;
  margin: 5px 0 0 0;
}
<div class="container">
  <div class="box"><span>short text</span></div>
  <div class="box"><span>centered text</span></div>
  <div class="box"><span>loooooooooooooooong text</span></div>
</div>
<p>&#8593;<br>true center</p>

Así es como funciona:

  • El div ( .container) de nivel superior es un contenedor flexible.
  • Cada elemento div ( .box) ahora es un elemento flexible.
  • Cada .boxelemento se entrega flex: 1para distribuir el espacio del contenedor por igual ( más detalles ).
  • Ahora los elementos están consumiendo todo el espacio en la fila y tienen el mismo ancho.
  • Haga que cada elemento sea un contenedor flexible (anidado) y agregue justify-content: center.
  • Ahora cada spanelemento es un elemento flexible centrado.
  • Use automárgenes flexibles para desplazar el exterior hacia spanla izquierda y hacia la derecha.

También puede renunciar justify-contenty usar automárgenes exclusivamente.

Pero justify-contentpuede funcionar aquí porque los automárgenes siempre tienen prioridad.

8.1. Alineándose con auto márgenes

Antes de la alineación a través de justify-contenty align-self, cualquier espacio libre positivo se distribuye a los márgenes automáticos en esa dimensión.

Michael Benjamin
fuente
55
Esta solución no permite widthque se especifique
Alex G
@AlexG, ¿cómo es eso? ¿Puede dar un ejemplo?
Michael Benjamin
44
Al cambiar el ancho de los elementos flexionados para distribuir el espacio, se pierde por completo la pregunta de OP. Su pregunta tiene espacio entre elementos. Ejemplo: "Imagine el siguiente diseño, donde los puntos representan el espacio entre los cuadros". Centrar texto en 3 elementos de tamaño uniforme es trivial.
Leland
1
¡Juro que podría besarte ahora mismo! Esto es exactamente lo que necesitaba y no podía entender.
eliotRosewater
1
Esto hizo exactamente lo que necesitaba para implementar barras de título de ventanas de estilo MacOS. Agregar white-space: nowrapfue la última pieza del rompecabezas para evitar que el texto se ajuste cuando el área se vuelve demasiado pequeña.
Geo1088
32
  1. Use tres artículos flexibles en el contenedor
  2. Establecer flex: 1en el primero y el último. Esto los hace crecer igualmente para llenar el espacio disponible dejado por el medio.
  3. Por lo tanto, el del medio tenderá a estar centrado.
  4. Sin embargo, si el primer o el último elemento tiene un contenido amplio, ese elemento flexible también crecerá debido al nuevo min-width: autovalor inicial.

    Tenga en cuenta que Chrome no parece implementar esto correctamente. Sin embargo, puede establecerlo min-widthen -webkit-max-contento -webkit-min-contenty funcionará también.

  5. Solo en ese caso el elemento del medio será empujado fuera del centro.

.outer-wrapper {
  display: flex;
}
.item {
  background: lime;
  margin: 5px;
}
.left.inner-wrapper, .right.inner-wrapper {
  flex: 1;
  display: flex;
  min-width: -webkit-min-content; /* Workaround to Chrome bug */
}
.right.inner-wrapper {
  justify-content: flex-end;
}
.animate {
  animation: anim 5s infinite alternate;
}
@keyframes anim {
  from { min-width: 0 }
  to { min-width: 100vw; }
}
<div class="outer-wrapper">
  <div class="left inner-wrapper">
    <div class="item animate">Left</div>
  </div>
  <div class="center inner-wrapper">
    <div class="item">Center</div>
  </div>
  <div class="right inner-wrapper">
    <div class="item">Right</div>
  </div>
</div>
<!-- Analogous to above --> <div class="outer-wrapper"><div class="left inner-wrapper"><div class="item">Left</div></div><div class="center inner-wrapper"><div class="item animate">Center</div></div><div class="right inner-wrapper"><div class="item">Right</div></div></div><div class="outer-wrapper"><div class="left inner-wrapper"><div class="item">Left</div></div><div class="center inner-wrapper"><div class="item">Center</div></div><div class="right inner-wrapper"><div class="item animate">Right</div></div></div>

Oriol
fuente
Gracias, esta respuesta responde con éxito la pregunta de los OP, a diferencia de la respuesta aceptada
Blowsie
8

En lugar de usar de manera predeterminada el uso de flexbox, el uso de la cuadrícula lo resuelve en 2 líneas de CSS sin marcado adicional dentro de los elementos secundarios de nivel superior.

HTML:

<header class="header">
  <div class="left">variable content</div>
  <div class="middle">variable content</div>
  <div class="right">variable content which happens to be very long</div>
</header>

CSS:

.header {
  display: grid;
  grid-template-columns: [first] 20% auto [last] 20%;
}
.middle {
  /* use either */
  margin: 0 auto;
  /* or */
  text-align: center;
}

Flexbox es genial, pero no debería ser la respuesta para todo. En este caso, la cuadrícula es claramente la opción más limpia.

Incluso hizo un codepen para su placer de prueba: https://codepen.io/anon/pen/mooQOV

Johan
fuente
2
+1 por admitir que puede haber otras opciones que no sean flexpara todo. Mi única objeción es que la alineación vertical se hace más fácil el uso de flex ( align-items: center) en caso de que sus artículos son diferentes alturas y necesidad de estar alineado
Felipe
1
Me gusta esta solución y la idea de usar la cuadrícula para este problema, pero creo que su solución tiene algunos inconvenientes notables. Primero, si el contenido de cualquiera de las celdas externas es mayor al 20%, el contenido se envolverá o se desbordará en la celda central. Segundo, si el centro es mayor al 60%, crecerá fuera de su caja de contenido y envolverá o empujará la última celda. Finalmente, el centro no se desplaza para dejar espacio para las celdas externas. Simplemente los obliga a envolverse / desbordarse. No es una mala solución, pero en mi opinión, no es "claramente la opción más limpia".
Chris
6

Puedes hacer esto así:

.bar {
    display: flex;    
    background: #B0BEC5;
}
.l {
    width: 50%;
    flex-shrink: 1;
    display: flex;
}
.l-content {
    background: #9C27B0;
}
.m { 
    flex-shrink: 0;
}
.m-content {    
    text-align: center;
    background: #2196F3;
}
.r {
    width: 50%;
    flex-shrink: 1;
    display: flex;
    flex-direction: row-reverse;
}
.r-content { 
    background: #E91E63;
}
<div class="bar">
    <div class="l">
        <div class="l-content">This is really long content.  More content.  So much content.</div>
    </div>
    <div class="m">
        <div class="m-content">This will always be in the center.</div>
    </div>
    <div class="r">
        <div class="r-content">This is short.</div>
    </div>
</div>

Michael Morton
fuente
2

Aquí hay una respuesta que usa cuadrícula en lugar de flexbox. Esta solución no requiere elementos de nieto adicionales en el HTML como lo hace la respuesta aceptada. Y funciona correctamente incluso cuando el contenido de un lado es lo suficientemente largo como para desbordarse hacia el centro, a diferencia de la respuesta de la cuadrícula de 2019.

Lo único que no hace esta solución es mostrar puntos suspensivos u ocultar el contenido adicional en el cuadro central, como se describe en la pregunta.

section {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
}

section > *:last-child {
  white-space: nowrap;
  text-align: right;
}

/* not essential; just for demo purposes */
section {
  background-color: #eee;
  font-family: helvetica, arial;
  font-size: 10pt;
  padding: 4px;
}

section > * {
  border: 1px solid #bbb;
  padding: 2px;
}
<section>
  <div>left</div>
  <div>center</div>
  <div>right side is longer</div>
</section>

<section>
  <div>left</div>
  <div>center</div>
  <div>right side is much, much longer</div>
</section>

<section>
  <div>left</div>
  <div>center</div>
  <div>right side is much, much longer, super long in fact</div>
</section>

Brian Morearty
fuente
1

Aquí hay otra forma de hacerlo, utilizando display: flexen los padres y los niños:

.Layout{
    display: flex;
    justify-content: center;
}
.Left{
    display: flex;
    justify-content: flex-start;
    width: 100%;
}
.Right{
    display: flex;
    justify-content: flex-end;
    width: 100%;
}
<div class = 'Layout'>
    <div class = 'Left'>I'm on the left</div>
    <div class = 'Mid'>Centered</div>
    <div class = 'Right'>I'm on the right</div>
</div>

Erik Martín Jordán
fuente
0

También puede usar esta forma simple de alcanzar la alineación central exacta para el elemento medio:

.container {
    display: flex;
    justify-content: space-between;
}

.container .sibling {
    display: flex;
    align-items: center;
    height: 50px;
    background-color: gray;
}

.container .sibling:first-child {
    width: 50%;
    display: flex;
    justify-content: space-between;
}

.container .sibling:last-child {
    justify-content: flex-end;
    width: 50%;
    box-sizing: border-box;
    padding-left: 100px; /* .center's width divided by 2 */
}

.container .sibling:last-child .content {
    text-align: right;
}

.container .sibling .center {
    height: 100%;
    width: 200px;
    background-color: lightgreen;
    transform: translateX(50%);
}

codepen: https://codepen.io/ErAz7/pen/mdeBKLG

Erfan Azary
fuente