将一个元素居中对齐,同时让其他弹性盒子元素左对齐或右对齐。

358

我想让ABC居中对齐。

如何使D完全向右对齐?

之前:

enter image description here

之后:

enter image description here

ul {
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
}
li {
  display: flex;
  margin: 1px;
  padding: 5px;
  background: #aaa;
}
li:last-child {
  background: #ddd;
  /* magic to throw to the right*/
}
<ul>
  <li>A</li>
  <li>B</li>
  <li>C</li>
  <li>D</li>
</ul>

https://jsfiddle.net/z44p7bsx/


"li"在其CSS规则中不需要显示:flex。 - undefined
11个回答

495

以下是实现此布局的五种选项:

  • CSS定位
  • 具有不可见DOM元素的Flexbox
  • 具有不可见伪元素的Flexbox
  • Flexbox使用flex: 1
  • CSS网格布局

方法1:CSS定位属性

position: relative应用于Flex容器。

position: absolute应用于项目D。

现在,该项目在Flex容器内部绝对定位。

更具体地说,项目D从文档流中移除,但保留在最近的定位祖先的范围内。

使用CSS偏移属性topright将此元素移动到位置。

li:last-child {
  position: absolute;
  top: 0;
  right: 0;
  background: #ddd;
}
ul {
  position: relative;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
}
li {
  display: flex;
  margin: 1px;
  padding: 5px;
  background: #aaa;
}
p {
  text-align: center;
  margin-top: 0;
}
span {
  background-color: aqua;
}
<ul>
  <li>A</li>
  <li>B</li>
  <li>C</li>
  <li>D</li>
</ul>
<p><span>true center</span></p>

注意:该方法的一个缺点是,某些浏览器可能无法完全将绝对定位的 flex 项目从正常流中删除。这会以一种非标准、意外的方式改变对齐方式。更多详情请参见:IE11 中不会从正常流中删除绝对定位的 flex 项


方法二:Flex 自动边距和不可见的 Flex 项(DOM 元素)

通过结合使用auto 边距和新的不可见 flex 项,可以实现布局。

新的 flex 项与项目 D 相同,并放置在相反的端点(左侧边缘)。

更具体地说,由于 flex 对齐基于自由空间的分布,所以新项是必要的平衡因子,以保持三个中间框水平居中。新项的宽度必须与现有的 D 项相同,否则中间的框将无法精确居中。

新项使用 visibility: hidden 从视图中删除。

简而言之:

  • 创建 D 元素的副本。
  • 将其放置在列表开头。
  • 使用 flex 的 auto 边距来使 ABC 居中,同时让两个 D 元素从两端创建平衡。
  • 对重复的 D 应用 visibility: hidden

li:first-child {
  margin-right: auto;
  visibility: hidden;
}
li:last-child {
  margin-left: auto;
  background: #ddd;
}
ul {
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
}
li {
  display: flex;
  margin: 1px;
  padding: 5px;
  background: #aaa;
}
p { text-align: center; margin-top: 0; }
span { background-color: aqua; }
<ul>
  <li>D</li><!-- new; invisible spacer item -->
  <li>A</li>
  <li>B</li>
  <li>C</li>
  <li>D</li>
</ul>
<p><span>true center</span></p>


方法三:弹性自动边距与不可见的弹性子项(伪元素)

该方法与方法二类似,但从语义上更为清晰,并且需要知道 D 的宽度。

  • 创建一个与 D 相同宽度的伪元素。
  • 使用 ::before 将其放在容器的开头。
  • 使用弹性布局的 auto 边距来使 ABC 保持完美居中,而伪元素和 D 元素则从两端创建相等的平衡。

ul::before {
  content:"D";
  margin: 1px auto 1px 1px;
  visibility: hidden;
  padding: 5px;
  background: #ddd;
}
li:last-child {
  margin-left: auto;
  background: #ddd;
}
ul {
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
}
li {
  display: flex;
  margin: 1px;
  padding: 5px;
  background: #aaa;
}
p { text-align: center; margin-top: 0; }
span { background-color: aqua; }
<ul>
  <li>A</li>
  <li>B</li>
  <li>C</li>
  <li>D</li>
</ul>
<p><span>true center</span></p>

方法四:在左右项目中添加flex: 1

在前面的第二或第三种方法的基础上,不需要担心左右项目的等宽以保持平衡,只需给每个项目添加flex: 1即可。这将强制它们都占用可用空间,从而使中间项居中。

然后,可以为单独的项目添加display: flex以对齐其内容。

注意 关于在容器上使用min-height与此方法一起使用的问题:目前在Chrome、Firefox、Edge和可能其他浏览器中,简写规则flex: 1会被分解成以下三个属性:

  • flex-grow: 1
  • flex-shrink: 1
  • flex-basis: 0%

flex-basis上的百分比单位(%)会导致当在容器上使用min-height时,此方法会出现问题。这是因为,通常情况下,子元素的百分比高度需要在父元素上设置一个显式的height属性。

这是一个旧的CSS规则,可以追溯到1998年(CSS Level 2),在许多浏览器中仍然有效。有关完整详情,请参见这里这里

以下是由user2651804在评论中发布的问题示例:

#flex-container {
  display: flex;
  flex-direction: column;
  background: teal;
  width: 150px;
  min-height: 80vh;
  justify-content: space-between;
}

#flex-container>div {
  background: orange;
  margin: 5px;
}

#flex-container>div:first-child {
  flex: 1;
}

#flex-container::after {
  content: "";
  flex: 1;
}
<div id="flex-container">
  <div>very long annoying text that will add on top of the height of its parent</div>
  <div>center</div>
</div>

解决方法是不使用百分比单位。尝试使用px或者什么都不用(尽管一些主流浏览器中至少有一些附加了百分比单位,但规范实际上推荐的是这样做的)。

#flex-container {
  display: flex;
  flex-direction: column;
  background: teal;
  width: 150px;
  min-height: 80vh;
  justify-content: space-between;
}

#flex-container > div {
  background: orange;
  margin: 5px;
}


/* OVERRIDE THE BROWSER SETTING IN THE FLEX PROPERTY */

#flex-container > div:first-child {
  flex: 1;
  flex-basis: 0;
}

#flex-container::after {
  content: "";
  flex: 1;
  flex-basis: 0;
}


/* OR... JUST SET THE LONG-HAND PROPERTIES INDIVIDUALLY

#flex-container > div:first-child {
  flex-grow: 1;
  flex-shrink: 1;
  flex-basis: 0;
}

#flex-container::after {
  content: "";
  flex-grow: 1;
  flex-shrink: 1;
  flex-basis: 0;
}
 */
<div id="flex-container">
  <div>very long annoying text that will add on top of the height of its parent</div>
  <div>center</div>
</div>

方法 #5: CSS Grid Layout

这可能是最干净和最有效的方法。不需要绝对定位、伪元素或其他hack技巧。

只需创建一个具有多列的网格,然后将项目放置在中间和末尾列中。基本上,只需留空第一列即可。


ul {
  display: grid;
  grid-template-columns: 1fr repeat(3, auto) 1fr;
  grid-column-gap: 5px;
  justify-items: center;
}

li:nth-child(1) { grid-column-start: 2; }
li:nth-child(4) { margin-left: auto; }

/* for demo only */
ul { padding: 0; margin: 0; list-style: none; }
li { padding: 5px; background: #aaa; }
p  { text-align: center; }
<ul>
  <li>A</li>
  <li>B</li>
  <li>C</li>
  <li>D</li>
</ul>
<p><span>| true center |</span></p>


1
为了使网格布局方法在垂直轴上工作,除了显而易见的之外,它只需要多一行代码: https://dev59.com/MHUOtIcB2Jgan1znu1MW - David Smith
嗨 @Michael Benjamin,我有一个关于BFC的问题——flex和grid项目是否保留在正常流中?我的疑惑主要是因为flex-direction: column会像块级格式化上下文一样在垂直方向上堆叠flex项目——提前感谢您的回答,我猜这条评论并不真正适合这个问题,但我需要像您一样擅长CSS的牛人。 - user19481364
1
@Daniel,也许你可以发一个带有示例的完整问题,这样你就可以得到一个完整的答案。但是,一般来说,除非你将绝对定位(即position: absoluteposition: fixed)应用于flex/grid项目,否则它仍然保持在正常文档流中。 - Michael Benjamin
1
太好了,@Michael Benjamin,那么它将仍然保持在正常流中,只是使用网格布局/弹性盒子布局(视情况而定),而不是块布局,对吧? - user19481364
你如何使用网格布局,将一个元素放在左侧,只有一个居中的元素放在中间?中心元素具有最大宽度和宽度:100%。 - Bru Mas Ribera
显示剩余3条评论

48
最简单的解决方案是将 justify-content 属性设置为 center,然后给第一个和最后一个子元素设置 margin-left:auto。

ul {
  display:flex;
  justify-content:center;
}


.a,.d {
  margin-left:auto;
}
<ul>
  <li class="a">A</li>
  <li>B</li>
  <li>C</li>
  <li class="d">D</li>
</ul>


@RedGuy11,你能分享一下你做了什么吗? - Suhail Akhtar
13
不幸的是,通过这种方法,A/B/C将在剩余空间内居中。换句话说,即使它们可以相对于整个宽度居中,也不会这样做。 - tremby

13

最简单的方式

.box{
  display:flex;
  justify-content:center;
}
.item1{
   flex:1;
   display: flex;
   justify-content: center;
   transform: translateX(10px);/*D element Width[if needed]*/
}
 <div class="box">
   <div class="item1">
      <div>A</div>
      <div>B</div>
      <div>C</div>
   </div>
   <div class="item2">D</div>
 </div>


20
使用元素的确切像素宽度可能会有问题。 - Drenai

9
使用display:grid方法,您可以将所有的ul子元素放入同一个单元格中,然后设置justify-self:

.ul {
  display: grid;
}

.ul > * {
  grid-column-start: 1;
  grid-row-start: 1;
  justify-self:center;
}

.ul > *:last-child {
  justify-self: right;
}

/* Make Fancy */
.li {
  display:inline-block;
  margin: 1px;
  padding: 5px;
  background: #bbb;
}
<div class='ul'>
  <span>
     <span class='li'>A</span>
     <span class='li'>B</span>
     <span class='li'>C</span>
  </span>
  <span class='li'>D</span>
</div>


@cdoublev 我将 ulli 更改为具有类的 div,以使其有效。 - Steven Spungin

0
如果您想使其对齐,可以简单地附加一个空的 span 并将三个子 span 分割到其中。

4
可以提供一个简短的演示来支持你的说法吗? - klewis
1
不要仅仅为了外观而使用额外的HTML,这可能会在其他浏览器中破坏您的页面。 - Hidayt Rahman

0

The easiest way:

.wrap {
    display:flex;
}
.full-width {
    width: 100%;
}
.centered {
    display: flex;
    justify-content:center;
}
.btn {
    display: flex;
    justify-content: end;
}
<div class="wrap">
   <div class="full-width"></div>
   <div class="full-width centered">
      <div>A</div>
      <div>B</div>
      <div>C</div>
   </div>
   <div class="full-width btn">D</div>
 </div>


-1
受到CSS Grid布局的第五种方法@Michal Benjamin解决方案的启发,因为我正在使用Tailwind,目前仍无法默认访问所有网格选项。这似乎有效:

ul {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

li {
  align-self: center;
}

li:nth-child(1) {
  justify-content: flex-start; /* OR margin-right: auto */
}

li:nth-child(3) {
  justify-content: flex-end; /* OR margin-left:auto */
}
<ul>
  <li>A</li>
  <li>B</li>
  <li>C</li>
</ul>

PS:不确定像这样混合使用 flex 和 grid 是否是一个好主意!


2
你没有混合使用网格和弹性盒子,justify-content是CSS网格属性和flexbox属性(align-self也一样)。 - Temani Afif
同时,网格和弹性布局旨在共同使用。 - TylerH
1
我试图在代码片段演示中理解你的CSS。你有无效的注释标记和一个杂乱的冒号。如果不正确,请随意进行修改。 - isherwood

-2
非常清晰的问题。经过几个小时的搜索,我忍不住要发布答案了。我们可以使用表格、表格单元格、绝对定位、变换来解决这个问题,但我们只需要使用 flexbox :)
.parent {
  display: flex;
  justify-content: flex-end;
}

.center {
  margin: auto;
}

http://codepen.io/rgfx/pen/BLorgd


8
这并没有真正将中间项目置于中心位置,因为它只考虑了剩余空间而非整个空间。http://codepen.io/anon/pen/QKGrRk - Michael Benjamin
@Michael_B,你是对的,我没有注意到,我的错。回到使用绝对位置:(也许你应该设置一个赏金来解决这个问题,因为你的声望很高。我想这样做,但是我的声望非常低,现在还更低了,因为我试图帮助你。 - rgfx
没有赏金会为flexbox模型添加新的行为,Michael_B给出的答案已经是最好的了...至少目前是这样。 - Asons

-2

接受的答案可以稍微改变一下,因为您可以使用网格模板区域,在不使用虚假元素的情况下完成它

grid-template-areas '. b c'
grid-template-columns: 1fr 1fr 1fr

随意复制代码片段并进行更改。此外,链接到特定答案比引用已过时的接受答案更好。 - isherwood

-4

我扩展了Michael_Banswer

.center-flex__2-of-3 > :nth-child(1), .center-flex__2-of-3 > :nth-child(3) {
  flex: 1;
}
.center-flex__2-of-3 > :nth-child(1) {
  justify-content: flex-start;
}
.center-flex__2-of-3 > :nth-child(3) {
  justify-content: flex-end;
}
.center-flex__1-of-2 > :nth-child(1) {
  margin: auto;
}
.center-flex__1-of-2 > :nth-child(2) {
  flex: 1;
  justify-content: flex-end;
}
.center-flex__2-of-2 > :nth-child(1) {
  flex: 1;
  justify-content: flex-start;
}
.center-flex__2-of-2 > :nth-child(2) {
  margin: auto;
}
.center-flex__1-of-2:before, .center-flex__1-of-1:before {
  content: '';
  flex: 1;
}
.center-flex__1-of-1:after, .center-flex__2-of-2:after {
  content: '';
  flex: 1;
}

[class*=center-flex] {
  display: flex;
  list-style: none;
  margin: 0;
  padding: 0;
  border: 10px solid rgba(0, 0, 0, 0.1);
}
[class*=center-flex] > * {
  display: flex;
}

li {
  padding: 3px 5px;
}
2 of 3
<ul class="center-flex__2-of-3">
  <span>
    <li>Accusamus</li>
    <li>Porro</li>
  </span>
  <span>
    <li>Center</li>
    <li>this</li>
  </span>
  <span>
    <li>Accusamus</li>
    <li>Porro</li>
    <li>Culpa</li>
    <li>Sit</li>
  </span>
</ul>

<br><br>
1 of 2
<ul class="akex center-flex__1-of-2">
  <span>
    <li>Center</li>
    <li>this</li>
  </span>
  <span>
    <li>Accusamus</li>
    <li>Porro</li>
    <li>Culpa</li>
    <li>Sit</li>
  </span>
</ul>

<br><br>
2 of 2
<ul class="akex center-flex__2-of-2">
  <span>
    <li>Accusamus</li>
    <li>Porro</li>
    <li>Culpa</li>
    <li>Sit</li>
  </span>
  <span>
    <li>Center</li>
    <li>this</li>
  </span>
</ul>

<br><br>
1 of 1
<ul class="center-flex__1-of-1">
  <span>
    <li>Center</li>
    <li>this</li>
  </span>
</ul>

在这里使用SASS作为codepen的帮助


1
您的列表标记无效。一个 span 不能是列表的子元素。 - isherwood

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接