Держите средний элемент по центру, когда боковые элементы имеют разную ширину

162

введите описание изображения здесь

Представьте себе следующий макет, где точки представляют пространство между полями:

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

Когда я удаляю правую коробку, мне нравится, чтобы центральная коробка все еще была в центре, вот так:

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

То же самое касается, если бы я удалил левую коробку.

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

Теперь, когда содержимое внутри центрального поля становится длиннее, оно занимает столько свободного места, сколько необходимо, оставаясь при этом центрированным. Левая и правая коробка никогда не будет сокращаться и , таким образом , когда , где нет места покинули overflow:hiddenи text-overflow: ellipsisпридет в действие , чтобы разорвать содержание;

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

Все вышесказанное - моя идеальная ситуация, но я не знаю, как этого добиться. Потому что, когда я создаю гибкую структуру, вот так:

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

Если левый и правый боксы будут одинакового размера, я получу желаемый эффект. Однако, когда один из двух имеет другой размер, центрированный прямоугольник больше не является центрированным.

Кто-нибудь может мне помочь?

Обновить

А justify-selfбыло бы неплохо, это было бы идеально:

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

.rightBox {
    justify-self : flex-end;
}
отметка
источник
3
В основном ... ты не можешь. Это не так, как flexboxдолжно работать. Вы можете попробовать другую методологию.
Paulie_D
1
Действительно закончил бы flexbox, если бы это сделал. Потому что flexbox - это все, что касается распределения пространства и того, как элементы ведут себя внутри.
Марк
1
Мы обсуждали justify-selfранее сегодня, поэтому вы можете найти это интересным: stackoverflow.com/questions/32551291/…
Майкл Бенджамин

Ответы:

130

Если левый и правый боксы будут одинакового размера, я получу желаемый эффект. Однако, когда один из двух имеет другой размер, центрированный прямоугольник больше не является центрированным. Кто-нибудь может мне помочь?

Вот метод, использующий flexbox для центрирования среднего элемента, независимо от ширины братьев и сестер.

Ключевая особенность:

  • чистый CSS
  • нет абсолютного позиционирования
  • нет JS / jQuery

Используйте вложенные гибкие контейнеры и autoполя:

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

Вот как это работает:

  • Div верхнего уровня (.container ) - это гибкий контейнер.
  • Каждый ребенок.box элемент ) теперь является гибким элементом.
  • Каждый .boxэлемент дан flex: 1для равномерного распределения пространства контейнера ( подробнее ).
  • Теперь элементы занимают все пространство в строке и имеют одинаковую ширину.
  • Сделайте каждый элемент (вложенным) гибким контейнером и добавьте justify-content: center .
  • Теперь каждый span элемент представляет собой центрированный гибкий элемент.
  • Используйте гибкие autoполя, чтобы сместить внешние spans влево и вправо.

Вы также можете отказаться justify-contentи использоватьauto поля исключительно.

Но justify-contentможет работать здесь, потому что autoполя всегда имеют приоритет.

8.1. Выравнивание по auto полям

До выравнивания через justify-contentи align-selfлюбое положительное свободное пространство распределяется по автоматическим полям в этом измерении.

Майкл Бенджамин
источник
5
Это решение не позволяет widthбыть уточненным
Alex G
@AlexG, как так? Можете ли вы привести пример?
Майкл Бенджамин
4
Изменение ширины согнутых элементов для равномерного распределения пространства полностью пропускает вопрос OP. У их вопроса есть пространство между элементами. Пример: «Представьте себе следующий макет, где точки представляют пространство между полями». Центрирование текста в 3 одинаковых по размеру элементах тривиально.
Леланд
1
Клянусь, я могу просто поцеловать тебя прямо сейчас! Это именно то, что мне было нужно, и я не мог понять.
eliotRosewater
1
Это именно то, что мне нужно для реализации заголовков окон в стиле MacOS. Добавление white-space: nowrapбыло последней частью головоломки, чтобы предотвратить перенос текста, когда область становится слишком маленькой.
Geo1088
32
  1. Используйте три гибких предмета в контейнере
  2. Установите flex: 1первый и последний из них. Это заставляет их расти одинаково, чтобы заполнить доступное пространство, оставленное средним.
  3. Таким образом, средний будет стремиться к центру.
  4. Однако, если первый или последний элемент имеет широкое содержимое, этот гибкий элемент также будет расти из-за нового min-width: autoначального значения.

    Примечание. Похоже, что в Chrome это реализовано неправильно. Тем не менее, вы можете установить min-widthна -webkit-max-contentили , -webkit-min-contentи это тоже будет работать.

  5. Только в этом случае средний элемент будет выталкиваться из центра.

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

Ориоль
источник
Спасибо, этот ответ успешно отвечает на вопрос ОП, в отличие от принятого ответа
Blowie
8

Вместо использования по умолчанию flexbox, использование grid решает его в 2 строки CSS без дополнительной разметки внутри дочерних элементов верхнего уровня.

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 качается, но не должен быть ответом за все. В этом случае сетка явно самый чистый вариант.

Даже сделал кодовую ручку для вашего удовольствия от тестирования: https://codepen.io/anon/pen/mooQOV

Johan
источник
2
+1 за признание того, что могут быть другие варианты, чем flexдля всего. Мое единственное возражение заключается в том, что вертикальное выравнивание становится проще с помощью flex ( align-items: center) в случае, если ваши элементы имеют разную высоту и должны быть выровнены
Felipe
1
Мне нравится это решение и идея использования сетки для этой проблемы, но я думаю, что у вашего решения есть некоторые заметные недостатки. Во-первых, если содержимое какой-либо из внешних ячеек превышает 20%, оно либо будет перенесено, либо переполнено в центральную ячейку. Во-вторых, если центр больше 60%, он вырастет за пределы своего поля содержимого и либо обернется, либо вытолкнет последнюю ячейку наружу. Наконец, центр не смещается, чтобы освободить место для внешних клеток. Это просто заставляет их обернуть / переполнить. Это не плохое решение, но IMO, это не «явно самый чистый вариант».
Крис
6

Вы можете сделать это так:

.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

Вот ответ, который использует сетку вместо flexbox. Это решение не требует дополнительных элементов внука в HTML, как принятый ответ. И он работает правильно, даже если содержимое на одной стороне становится достаточно длинным, чтобы перетекать в центр, в отличие от ответа сетки в 2019 году.

Единственное, что не может сделать это решение - показать многоточие или скрыть дополнительный контент в центральном окне, как описано в вопросе.

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

Вот еще один способ сделать это, используя display: flexв родителей и детей:

.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

Вы также можете использовать этот простой способ для достижения точного выравнивания по центру для среднего элемента:

.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

Эрфан Азари
источник