以下是实现此布局的五种选项:
- CSS定位
- 具有不可见DOM元素的Flexbox
- 具有不可见伪元素的Flexbox
- Flexbox使用
flex: 1
- CSS网格布局
方法1:CSS定位属性
将position: relative
应用于Flex容器。
将position: absolute
应用于项目D。
现在,该项目在Flex容器内部绝对定位。
更具体地说,项目D从文档流中移除,但保留在最近的定位祖先的范围内。
使用CSS偏移属性top
和right
将此元素移动到位置。
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
边距来使 A
、B
和 C
居中,同时让两个 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>
<li>A</li>
<li>B</li>
<li>C</li>
<li>D</li>
</ul>
<p><span>true center</span></p>
方法三:弹性自动边距与不可见的弹性子项(伪元素)
该方法与方法二类似,但从语义上更为清晰,并且需要知道 D
的宽度。
- 创建一个与
D
相同宽度的伪元素。
- 使用
::before
将其放在容器的开头。
- 使用弹性布局的
auto
边距来使 A
、B
和 C
保持完美居中,而伪元素和 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;
}
#flex-container > div:first-child {
flex: 1;
flex-basis: 0;
}
#flex-container::after {
content: "";
flex: 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; }
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>