Mantenha o item do meio centralizado quando os itens laterais tiverem larguras diferentes

162

insira a descrição da imagem aqui

Imagine o seguinte layout, onde os pontos representam o espaço entre as caixas:

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

Quando removo a caixa direita, gosto que a caixa central ainda esteja no centro, da seguinte forma:

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

O mesmo vale para se eu remover a caixa esquerda.

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

Agora, quando o conteúdo dentro da caixa central ficar mais longo, ele ocupará o espaço disponível necessário, enquanto permanece centralizado. A caixa de esquerda e direita nunca vai encolher e, assim, quando, onde há espaço deixado o overflow:hiddene text-overflow: ellipsisvirão em vigor para quebrar o conteúdo;

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

Tudo acima é a minha situação ideal, mas não tenho idéia de como conseguir esse efeito. Porque quando eu crio uma estrutura flexível assim:

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

Se as caixas esquerda e direita tiverem exatamente o mesmo tamanho, obtenho o efeito desejado. No entanto, quando um dos dois é de tamanho diferente, a caixa centralizada não está mais realmente centralizada.

Existe alguém que possa me ajudar?

Atualizar

A justify-selfseria bom, isso seria ideal:

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

.rightBox {
    justify-self : flex-end;
}
Marca
fonte
3
Basicamente ... você não pode. Não flexboxé assim que deve funcionar. Você pode tentar outra metodologia.
Paulie_D
1
Realmente completaria o flexbox se o fizesse. Causa O flexbox tem a ver com distribuir espaço e como os itens se comportam dentro.
Mark
1
Discutimos justify-selfhoje mais cedo, então você pode achar isso interessante: stackoverflow.com/questions/32551291/…
Michael Benjamin

Respostas:

130

Se as caixas esquerda e direita tiverem exatamente o mesmo tamanho, obtenho o efeito desejado. No entanto, quando um dos dois é de tamanho diferente, a caixa centralizada não está mais realmente centralizada. Existe alguém que possa me ajudar?

Aqui está um método usando o flexbox para centralizar o item do meio, independentemente da largura dos irmãos.

Características principais:

  • CSS puro
  • nenhum posicionamento absoluto
  • sem JS / jQuery

Use contêineres flexíveis e automargens aninhados :

.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>

Veja como funciona:

  • O div ( .container) de nível superior é um contêiner flexível.
  • Cada div ( .box) filho agora é um item flexível.
  • Cada .boxitem é fornecido flex: 1para distribuir o espaço do contêiner igualmente ( mais detalhes ).
  • Agora os itens estão consumindo todo o espaço na linha e têm a mesma largura.
  • Faça de cada item um contêiner flexível (aninhado) e adicione justify-content: center.
  • Agora cada spanelemento é um item flexível centralizado.
  • Use automargens flexíveis para deslocar os botões externos para spana esquerda e direita.

Você também pode renunciar justify-contente usar automargens exclusivamente.

Mas justify-contentpode funcionar aqui porque as automargens sempre têm prioridade.

8.1 Alinhando com auto margens

Antes do alinhamento via justify-contente align-self, qualquer espaço livre positivo é distribuído para as margens automáticas nessa dimensão.

Michael Benjamin
fonte
5
Esta solução não permite widthque seja especificado #
Alex G
@AlexG, como assim? você pode dar um exemplo?
Michael Benjamin
4
Alterar as larguras dos elementos flexionados para distribuir o espaço equivale totalmente à pergunta do OP. A pergunta deles tem espaço entre os elementos. Ex: "Imagine o seguinte layout, onde os pontos representam o espaço entre as caixas". Centrar o texto em três elementos de tamanho uniforme é trivial.
Leland
1
Eu juro que poderia apenas te beijar agora! Isso é exatamente o que eu precisava e não consegui descobrir.
EliotRosewater
1
Isso fez exatamente o que eu precisava para implementar as barras de título das janelas no estilo MacOS. A adição white-space: nowrapfoi a última peça do quebra-cabeça para impedir a quebra de texto quando a área ficar muito pequena.
Geo1088 27/02
32
  1. Use três itens flexíveis no contêiner
  2. Defina flex: 1para o primeiro e o último. Isso os faz crescer igualmente para preencher o espaço disponível deixado pelo meio.
  3. Assim, o do meio tende a estar centrado.
  4. No entanto, se o primeiro ou o último item tiver um conteúdo amplo, esse item flexível também aumentará devido ao novo min-width: autovalor inicial.

    Nota O Chrome parece não implementar isso corretamente. No entanto, você pode definir min-widthcomo -webkit-max-contentou -webkit-min-contente funcionará também.

  5. Somente nesse caso o elemento do meio será empurrado para fora do 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
fonte
Obrigado, esta resposta responde com êxito à pergunta dos OPs, diferentemente da resposta aceita.
Blowsie 14/07
8

Em vez de usar o flexbox como padrão, o uso da grade o resolve em 2 linhas de CSS sem marcação adicional dentro dos filhos de nível 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;
}

O Flexbox é ótimo, mas não deve ser a resposta para tudo. Nesse caso, a grade é claramente a opção mais limpa.

A Even fez um codepen para o seu prazer em testar: https://codepen.io/anon/pen/mooQOV

Johan
fonte
2
+1 por admitir que pode haver outras opções além flexde tudo. Minha única objeção é que alinhamento vertical se torna mais fácil usando o flex ( align-items: center) no caso de seus itens são diferentes alturas e precisam ser alinhados
Felipe
1
Gosto dessa solução e da idéia de usar a grade para esse problema, mas acho que sua solução tem algumas desvantagens notáveis. Primeiro, se o conteúdo de qualquer célula externa for maior que 20%, o conteúdo será encapsulado ou transbordará para a célula central. Segundo, se o centro for maior que 60%, crescerá fora da caixa de conteúdo e envolverá ou empurrará a última célula para fora. Finalmente, o centro não muda para dar espaço às células externas. Apenas os força a quebrar / transbordar. Não é uma solução ruim, mas, na IMO, não é "claramente a opção mais limpa".
22419 Chris
6

Você pode fazer o seguinte:

.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
fonte
2

Aqui está uma resposta que usa grade em vez de flexbox. Esta solução não requer elementos netos extras no HTML, como a resposta aceita. E funciona corretamente mesmo quando o conteúdo de um lado é longo o suficiente para transbordar para o centro, ao contrário da resposta da grade de 2019.

A única coisa que essa solução não faz é mostrar reticências ou ocultar o conteúdo extra na caixa central, conforme descrito na pergunta.

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
fonte
1

Aqui está outra maneira de fazer isso, usando display: flexnos pais e filhos:

.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
fonte
0

você também pode usar esta maneira simples de atingir o alinhamento exato do centro do elemento do meio:

.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
fonte