Utrzymuj środkowy element wyśrodkowany, gdy elementy boczne mają różne szerokości

162

wprowadź opis obrazu tutaj

Wyobraź sobie następujący układ, w którym kropki reprezentują przestrzeń między polami:

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

Po usunięciu prawego pudełka chcę, aby środkowe pudełko nadal znajdowało się na środku, na przykład:

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

To samo dotyczy sytuacji, gdybym usunął lewe pudełko.

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

Teraz, gdy zawartość w środkowym polu wydłuży się, zajmie tyle dostępnego miejsca, ile potrzeba, pozostając wyśrodkowana. Okno w lewo i prawo nie będzie się kurczyć, a więc kiedy, gdzie nie ma miejsca opuścił overflow:hiddeni text-overflow: ellipsiswejdzie w efekcie przełamać zawartości;

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

To wszystko jest moją idealną sytuacją, ale nie mam pojęcia, jak osiągnąć ten efekt. Ponieważ kiedy tworzę taką strukturę flex:

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

Jeśli lewe i prawe pudełko będą dokładnie tej samej wielkości, uzyskam pożądany efekt. Jednak gdy jedno z dwóch ma inny rozmiar, wyśrodkowane pudełko nie jest już naprawdę wyśrodkowane.

Czy jest ktoś, kto może mi pomóc?

Aktualizacja

justify-selfByłoby miło, to byłoby idealne:

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

.rightBox {
    justify-self : flex-end;
}
znak
źródło
3
Zasadniczo ... nie możesz. Nie tak flexboxpowinno działać. Możesz wypróbować inną metodologię.
Paulie_D
1
Naprawdę zakończyłby flexbox, gdyby tak się stało. Ponieważ flexbox polega na dystrybucji przestrzeni i zachowaniu się elementów w środku.
Mark
1
Rozmawialiśmy justify-selfwcześniej dzisiaj, więc może ci się wydać to interesujące: stackoverflow.com/questions/32551291/…
Michael Benjamin

Odpowiedzi:

130

Jeśli lewe i prawe pudełko byłyby dokładnie tej samej wielkości, uzyskam pożądany efekt. Jednak gdy jedno z dwóch ma inny rozmiar, wyśrodkowane pudełko nie jest już naprawdę wyśrodkowane. Czy jest ktoś, kto może mi pomóc?

Oto metoda wykorzystująca flexbox do wyśrodkowania środkowego elementu, niezależnie od szerokości rodzeństwa.

Kluczowe cechy:

  • czysty CSS
  • brak pozycjonowania absolutnego
  • brak JS / jQuery

Użyj zagnieżdżonych kontenerów elastycznych i automarginesów:

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

Oto jak to działa:

  • Element div ( .container) najwyższego poziomu to kontener elastyczny.
  • Każdy .boxelement podrzędny div ( ) jest teraz elementem elastycznym.
  • Każdy .boxprzedmiot jest podany flex: 1w celu równomiernego rozłożenia powierzchni pojemnika ( więcej szczegółów ).
  • Teraz przedmioty zajmują całą przestrzeń w rzędzie i mają jednakową szerokość.
  • Uczyń każdy element (zagnieżdżonym) kontenerem elastycznym i dodaj justify-content: center.
  • Teraz każdy spanelement jest wyśrodkowanym elementem elastycznym.
  • Użyj elastycznych automarginesów, aby przesunąć zewnętrzne spans w lewo i w prawo.

Możesz także zrezygnować justify-contenti używać autowyłącznie marginesów.

Ale justify-contentmoże działać tutaj, ponieważ automarginesy zawsze mają priorytet.

8.1. Wyrównywanie z auto marginesami

Przed wyrównywaniem przez justify-contenti align-selfkażda dodatnia wolna przestrzeń jest rozdzielana na automatyczne marginesy w tym wymiarze.

Michael Benjamin
źródło
5
To rozwiązanie nie pozwala widthna określenie
Alex G
@AlexG, jak to? Czy możesz podać przykład?
Michael Benjamin
4
Zmiana szerokości wygiętych elementów w celu równomiernego rozłożenia przestrzeni całkowicie pomija pytanie OP. Ich pytanie ma przestrzeń między elementami. Np. „Wyobraź sobie następujący układ, w którym kropki oznaczają przestrzeń między polami”. Wyśrodkowanie tekstu w 3 równych elementach jest trywialne.
Leland
1
Przysięgam, że mógłbym cię teraz po prostu pocałować! To jest dokładnie to, czego potrzebowałem i nie mogłem zrozumieć.
eliotRosewater
1
Zrobiło to dokładnie to, czego potrzebowałem do zaimplementowania pasków tytułowych okien w stylu MacOS. Dodanie white-space: nowrapbyło ostatnim elementem układanki, aby zapobiec zawijaniu się tekstu, gdy obszar stanie się zbyt mały.
Geo1088
32
  1. Użyj trzech elementów elastycznych w kontenerze
  2. Ustaw flex: 1na pierwszy i ostatni. To sprawia, że ​​rosną równomiernie, aby wypełnić dostępną przestrzeń pozostawioną przez środkową.
  3. Zatem środkowy będzie miał tendencję do wyśrodkowania.
  4. Jeśli jednak pierwszy lub ostatni element ma szeroką zawartość, ten element elastyczny również wzrośnie ze względu na nową min-width: autowartość początkową.

    Uwaga Wydaje się, że Chrome nie implementuje tego poprawnie. Jednakże, można ustawić min-widthna -webkit-max-contentlub -webkit-min-contenti to będzie działać zbyt.

  5. Tylko w takim przypadku środkowy element zostanie wypchnięty ze środka.

.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
źródło
Dzięki, ta odpowiedź z powodzeniem odpowiada na pytanie PO, w przeciwieństwie do zaakceptowanej odpowiedzi
Blowsie
8

Zamiast domyślnie używać flexboksa, użycie gridu rozwiązuje to w 2 liniach CSS bez dodatkowych znaczników w elementach potomnych najwyższego poziomu.

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 rządzi, ale nie powinien być odpowiedzią na wszystko. W tym przypadku kratka jest zdecydowanie najczystszą opcją.

Zrobiłem nawet kod dla przyjemności testowania: https://codepen.io/anon/pen/mooQOV

Johan
źródło
2
+1 za przyznanie się, że mogą istnieć inne opcje niż flexwszystko. Mój jedyny zarzut to, że pionowe wyrównanie staje się łatwiejsze przy użyciu Flex ( align-items: center) w przypadku, gdy elementy są różne wysokości i muszą być wyrównane
Felipe
1
Podoba mi się to rozwiązanie i pomysł wykorzystania siatki do tego problemu, ale myślę, że Twoje rozwiązanie ma kilka istotnych wad. Po pierwsze, jeśli zawartość którejkolwiek z zewnętrznych komórek jest większa niż 20%, zawartość zawinie się lub przeleje do środkowej komórki. Po drugie, jeśli środek jest większy niż 60%, wyrośnie poza pole zawartości i albo zawinie, albo wypchnie ostatnią komórkę. Wreszcie środek nie przesuwa się, aby zrobić miejsce dla zewnętrznych komórek. Po prostu zmusza je do zawijania / przepełnienia. To nie jest złe rozwiązanie, ale IMO, nie jest to „najwyraźniej najczystsza opcja”.
Chris
6

Możesz to zrobić w następujący sposób:

.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
źródło
2

Oto odpowiedź wykorzystująca grid zamiast flexbox. To rozwiązanie nie wymaga dodatkowych elementów wnuków w kodzie HTML, jak to ma miejsce w zaakceptowanej odpowiedzi. I działa poprawnie nawet wtedy, gdy zawartość po jednej stronie staje się wystarczająco długa, aby przelewać się do środka, w przeciwieństwie do odpowiedzi siatki z 2019 r.

Jedyną rzeczą, której to rozwiązanie nie robi, jest wyświetlenie wielokropka lub ukrycie dodatkowej zawartości w środkowym polu, jak opisano w pytaniu.

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
źródło
1

Oto inny sposób, aby to zrobić, używając display: flexu rodziców i dzieci:

.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
źródło
0

możesz również użyć tego prostego sposobu, aby uzyskać dokładne wyrównanie do środka środkowego elementu:

.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
źródło