Mantieni l'elemento centrale centrato quando gli oggetti laterali hanno larghezze diverse


162

inserisci qui la descrizione dell'immagine

Immagina il seguente layout, in cui i punti rappresentano lo spazio tra le caselle:

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

Quando rimuovo la casella giusta, mi piace che la casella centrale sia ancora al centro, in questo modo:

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

Lo stesso vale per rimuovere la casella a sinistra.

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

Ora, quando il contenuto all'interno del riquadro centrale diventa più lungo, occuperà lo spazio disponibile necessario rimanendo centrato. Le caselle sinistra e destra non si restringono mai e quindi quando non c'è spazio lasciato overflow:hiddene text-overflow: ellipsisentrerà in vigore per interrompere il contenuto;

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

Tutto quanto sopra è la mia situazione ideale, ma non ho idea di come realizzare questo effetto. Perché quando creo una struttura flessibile in questo modo:

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

Se la casella sinistra e destra avessero esattamente le stesse dimensioni, ottengo l'effetto desiderato. Tuttavia, quando uno dei due ha dimensioni diverse, la casella centrata non è più realmente centrata.

C'è qualcuno che mi può aiutare?

Aggiornare

A justify-selfsarebbe bello, questo sarebbe l'ideale:

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

.rightBox {
    justify-self : flex-end;
}

3
Fondamentalmente ... non puoi. Non è così che flexboxdovrebbe funzionare. Potresti provare un'altra metodologia.
Paulie_D,

1
Completerebbe davvero la flexbox se lo facesse. Perché la flexbox si basa sulla distribuzione dello spazio e sul modo in cui gli oggetti si comportano all'interno.
Segna il

1
Ne abbiamo discusso justify-selfprima oggi, quindi potresti trovare questo interessante: stackoverflow.com/questions/32551291/…
Michael Benjamin,

Risposte:


130

Se le caselle sinistra e destra avessero esattamente le stesse dimensioni, ottengo l'effetto desiderato. Tuttavia, quando uno dei due ha dimensioni diverse, la casella centrata non è più realmente centrata. C'è qualcuno che mi può aiutare?

Ecco un metodo che utilizza flexbox per centrare l'elemento centrale, indipendentemente dalla larghezza dei fratelli.

Caratteristiche principali:

  • CSS puro
  • nessun posizionamento assoluto
  • no JS / jQuery

Utilizzare i contenitori e i automargini flessibili nidificati :

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

Ecco come funziona:

  • Il div ( .container) di livello superiore è un contenitore flessibile.
  • Ogni div ( .box) figlio ora è un oggetto flessibile.
  • Ogni .boxarticolo viene fornito flex: 1allo scopo di distribuire equamente lo spazio contenitore ( maggiori dettagli ).
  • Ora gli oggetti stanno consumando tutto lo spazio nella riga e hanno la stessa larghezza.
  • Trasforma ogni oggetto in un contenitore flessibile (nidificato) e aggiungi justify-content: center.
  • Ora ogni spanelemento è un oggetto flessibile centrato.
  • Utilizzare i automargini di flessione per spostare l'esterno a spansinistra e a destra.

È inoltre possibile rinunciare justify-contente utilizzare autoesclusivamente i margini.

Ma justify-contentpuò funzionare qui perché i automargini hanno sempre la priorità.

8.1. Allineando con i auto margini

Prima dell'allineamento tramite justify-contente align-self, qualsiasi spazio libero positivo viene distribuito ai margini automatici in quella dimensione.


5
Questa soluzione non consente widthdi essere specificata
Alex G,

@AlexG, come mai? Puoi fornire un esempio?
Michael Benjamin,

4
Cambiare le larghezze degli elementi flessi per distribuire lo spazio equamente manca completamente alla domanda di OP. La loro domanda ha spazio tra gli elementi. Esempio: "Immagina il seguente layout, in cui i punti rappresentano lo spazio tra le caselle". Centrare il testo in 3 elementi di dimensioni uniformi è banale.
Leland,

1
Giuro che potrei baciarti proprio ora! Questo è esattamente ciò di cui avevo bisogno e che non sono riuscito a capire.
eliotRosewater

1
Questo ha fatto esattamente ciò di cui avevo bisogno per implementare le barre dei titoli delle finestre in stile MacOS. L'aggiunta è white-space: nowrapstata l'ultima parte del puzzle per evitare che il testo si avvolga quando l'area diventa troppo piccola.
Geo1088,

32
  1. Usa tre oggetti flessibili nel contenitore
  2. Imposta flex: 1il primo e l'ultimo. Questo li fa crescere allo stesso modo per riempire lo spazio disponibile lasciato da quello centrale.
  3. Pertanto, quello centrale tenderà ad essere centrato.
  4. Tuttavia, se il primo o l'ultimo articolo ha un contenuto ampio, anche quell'elemento flessibile aumenterà a causa del nuovo min-width: autovalore iniziale.

    Nota Chrome non sembra implementarlo correttamente. Tuttavia, puoi impostare min-widthsu -webkit-max-contento -webkit-min-contente funzionerà anche.

  5. Solo in tal caso l'elemento centrale verrà espulso dal 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>


Grazie, questa risposta risponde correttamente alla domanda dei PO, a differenza della risposta accettata
Blowsie

8

Invece di utilizzare per impostazione predefinita flexbox, l'utilizzo della griglia lo risolve in 2 righe di CSS senza markup aggiuntivo all'interno dei figli di livello superiore.

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 oscilla ma non dovrebbe essere la risposta a tutto. In questo caso la griglia è chiaramente l'opzione più pulita.

Ho persino creato un codice per il tuo piacere di test: https://codepen.io/anon/pen/mooQOV


2
+1 per ammettere che potrebbero esserci altre opzioni oltre flexa tutto. La mia unica obiezione è che l'allineamento verticale diventa più facile usando flex ( align-items: center) nel caso in cui i tuoi oggetti abbiano altezze diverse e debbano essere allineati
Felipe,

1
Mi piace questa soluzione e l'idea di utilizzare la griglia per questo problema, ma penso che la tua soluzione abbia alcuni notevoli inconvenienti. Innanzitutto, se uno dei contenuti delle celle esterne è maggiore del 20%, il contenuto verrà spostato o traboccato nella cella centrale. In secondo luogo, se il centro è più grande del 60%, crescerà al di fuori della sua casella di contenuto e avvolgerà o spingerà fuori l'ultima cella. Infine, il centro non si sposta per fare spazio alle celle esterne. Li costringe semplicemente a avvolgere / traboccare. Non è una cattiva soluzione, ma IMO, non è "chiaramente l'opzione più pulita".
Chris,

6

Puoi farlo in questo modo:

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


2

Ecco una risposta che utilizza grid anziché flexbox. Questa soluzione non richiede elementi nipoti aggiuntivi nell'HTML come fa la risposta accettata. E funziona correttamente anche quando il contenuto su un lato diventa abbastanza lungo da traboccare al centro, a differenza della risposta alla griglia del 2019.

L'unica cosa che questa soluzione non fa è mostrare i puntini di sospensione o nascondere il contenuto extra nella casella centrale, come descritto nella domanda.

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>


1

Ecco un altro modo per farlo, usando display: flexnei genitori e nei figli:

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


0

puoi anche usare questo semplice modo per raggiungere l'esatto allineamento centrale per l'elemento centrale:

.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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.