Gardez l'élément du milieu centré lorsque les éléments latéraux ont des largeurs différentes

162

entrez la description de l'image ici

Imaginez la disposition suivante, où les points représentent l'espace entre les cases:

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

Lorsque je retire la bonne boîte, j'aime que la boîte centrale soit toujours au centre, comme ceci:

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

Il en va de même si je supprimais la case de gauche.

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

Désormais, lorsque le contenu de la zone centrale s'allonge, il occupera autant d'espace disponible que nécessaire tout en restant centré. La boîte de gauche et de droite ne rétrécira jamais et donc quand il n'y a plus d'espace, le overflow:hiddenet text-overflow: ellipsisentrera en vigueur pour casser le contenu;

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

Tout ce qui précède est ma situation idéale, mais je ne sais pas comment accomplir cet effet. Parce que quand je crée une structure flex comme ça:

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

Si les cases gauche et droite avaient exactement la même taille, j'obtiens l'effet désiré. Cependant, lorsque l'un des deux est d'une taille différente, la boîte centrée n'est plus vraiment centrée.

Y a-t-il quelqu'un qui peut m'aider?

Mettre à jour

Ce justify-selfserait bien, ce serait idéal:

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

.rightBox {
    justify-self : flex-end;
}
marque
la source
3
En gros ... vous ne pouvez pas. Ce n'est pas ainsi que cela flexboxest censé fonctionner. Vous pourriez essayer une autre méthodologie.
Paulie_D
1
Serait vraiment une flexbox complète si elle le faisait. Parce que flexbox est tout au sujet de la distribution de l'espace et de la façon dont les éléments se comportent à l'intérieur.
Marquez le
1
Nous en avons discuté justify-selfplus tôt dans la journée, vous pourriez donc trouver cela intéressant: stackoverflow.com/questions/32551291/…
Michael Benjamin

Réponses:

130

Si les cases gauche et droite avaient exactement la même taille, j'obtiens l'effet souhaité. Cependant, lorsque l'un des deux a une taille différente, la boîte centrée n'est plus vraiment centrée. Y a-t-il quelqu'un qui peut m'aider?

Voici une méthode utilisant flexbox pour centrer l'élément du milieu, quelle que soit la largeur des frères et sœurs.

Principales caractéristiques:

  • CSS pur
  • pas de positionnement absolu
  • pas de JS / jQuery

Utilisez des conteneurs flexibles imbriqués et des automarges:

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

Voici comment ça fonctionne:

  • Le div ( .container) de niveau supérieur est un conteneur flex.
  • Chaque div enfant ( .box) est maintenant un élément flexible.
  • Chaque .boxélément est donné flex: 1afin de répartir équitablement l'espace du conteneur ( plus de détails ).
  • Désormais, les éléments consomment tout l'espace de la ligne et ont la même largeur.
  • Faites de chaque élément un conteneur flexible (imbriqué) et ajoutez-le justify-content: center.
  • Désormais, chaque spanélément est un élément flex centré.
  • Utilisez les automarges flexibles pour décaler les côtés extérieurs vers la spangauche et vers la droite.

Vous pouvez également renoncer justify-contentet utiliser autoexclusivement les marges.

Mais justify-content peut fonctionner ici car les automarges ont toujours la priorité.

8.1. Alignement avec les auto marges

Avant l'alignement via justify-contentet align-self, tout espace libre positif est distribué aux marges automatiques dans cette dimension.

Michael Benjamin
la source
5
Cette solution ne permet pas widthd'être spécifié
Alex G
@AlexG, comment ça? Pouvez vous donner un exemple?
Michael Benjamin
4
Changer les largeurs des éléments fléchis pour répartir l'espace de manière égale passe entièrement à côté de la question d'OP. Leur question a un espace entre les éléments. Ex: "Imaginez la disposition suivante, où les points représentent l'espace entre les cases." Centrer du texte dans 3 éléments de taille égale est trivial.
Leland
1
Je jure que je pourrais juste t'embrasser maintenant! C'est exactement ce dont j'avais besoin et je n'ai pas pu comprendre.
eliotRosewater
1
Cela a fait exactement ce dont j'avais besoin pour implémenter les barres de titre de fenêtre de style MacOS. L'ajout white-space: nowrapétait la dernière pièce du puzzle pour empêcher le texte de s'habiller lorsque la zone devient trop petite.
Geo1088
32
  1. Utilisez trois éléments flexibles dans le conteneur
  2. Réglez flex: 1sur le premier et le dernier. Cela les fait croître de manière égale pour remplir l'espace disponible laissé par celui du milieu.
  3. Ainsi, celui du milieu aura tendance à être centré.
  4. Cependant, si le premier ou le dernier élément a un contenu large, cet élément flexible augmentera également en raison de la nouvelle min-width: autovaleur initiale.

    Remarque Chrome ne semble pas l'implémenter correctement. Cependant, vous pouvez définir min-widthsur -webkit-max-contentou -webkit-min-contentet cela fonctionnera également.

  5. Ce n'est que dans ce cas que l'élément central sera poussé hors du centre.

.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
la source
Merci, cette réponse répond avec succès à la question OP, contrairement à la réponse acceptée
Blowsie le
8

Au lieu d'utiliser flexbox par défaut, l'utilisation de la grille le résout en 2 lignes de CSS sans balisage supplémentaire à l'intérieur des enfants de niveau supérieur.

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 bascule mais ne devrait pas être la solution à tout. Dans ce cas, la grille est clairement l'option la plus propre.

Même fait un codepen pour votre plaisir de test: https://codepen.io/anon/pen/mooQOV

Johan
la source
2
+1 pour admettre qu'il peut y avoir d'autres options que flexpour tout. Ma seule objection est que l'alignement vertical devient plus facile en utilisant flex ( align-items: center) au cas où vos éléments sont de différentes hauteurs et doivent être alignés
Felipe
1
J'aime cette solution et l'idée d'utiliser la grille pour ce problème, mais je pense que votre solution présente des inconvénients notables. Premièrement, si le contenu de l'une ou l'autre des cellules externes est supérieur à 20%, le contenu va soit s'enrouler soit déborder dans la cellule centrale. Deuxièmement, si le centre est plus grand que 60%, il va se développer en dehors de sa zone de contenu et soit envelopper ou pousser la dernière cellule. Enfin, le centre ne se déplace pas pour faire de la place aux cellules extérieures. Cela les oblige simplement à emballer / déborder. Ce n'est pas une mauvaise solution, mais IMO, ce n'est pas "clairement l'option la plus propre".
Chris
6

Vous pouvez faire ceci comme ceci:

.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
la source
2

Voici une réponse qui utilise la grille au lieu de la flexbox. Cette solution ne nécessite pas d'éléments petits-enfants supplémentaires dans le HTML comme le fait la réponse acceptée. Et cela fonctionne correctement même lorsque le contenu d'un côté est suffisamment long pour déborder vers le centre, contrairement à la réponse de la grille de 2019.

La seule chose que cette solution ne fait pas est d'afficher des points de suspension ou de masquer le contenu supplémentaire dans la zone centrale, comme décrit dans la question.

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
la source
1

Voici une autre façon de le faire, en utilisant display: flexchez les parents et les enfants:

.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
la source
0

vous pouvez également utiliser cette méthode simple pour atteindre l'alignement central exact de l'élément central:

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