CSS 怎么实现两个 div 一个固定宽另一个填充剩余空间?
当年这可能是一个典型的CSS面试题:“两列布局”。很多年前(大约是10年前吧),有过这样的一个面试题:
两列布局,左侧列宽度是20%,右侧列是80%,两列之间的间距是20px,CSS怎样实现这样的一个布局,而且不会出现滚动条!
对于题主这个问题,早在当年可能会较为蛋疼一点,或许很多开发者会采用 JavaScript 来辅助完成。但对于今天而言,这个布局效果再简单不过了。而且方案很多。
假设构建这样的一个布局,它的 HTML像下面这样:
<body>
<aside>侧边栏,固定宽度</aside>
<main>主内容,填充剩余空间</main>
</body>
这个布局最大的特点是:
两列布局,其中一列固定尺寸(比如说侧边栏),另外一列自适应,能随着容器宽度调整!
接下来,分不同方案来构建这样的一个布局。
calc()方案
在Flexbox和Grid还没出现的时候,如果不借助 JavaScript 的话,calc()
应该是最简单的了吧 :
body {
display: flow-root; /*清除浮动*/
}
aside {
float: left;
width: 220px;
}
main {
float: left;
width: calc(100% - 220px);
}
如果你还希望列与列之间有一定的间距,还可以像下面样写:
body {
display: flow-root; /*清除浮动*/
}
aside {
float: left;
width: 220px;
}
main {
float: left;
margin-left: 20px;
width: calc(100% - 220px - 20px);
}
结合 CSS 自定义属性,灵活性会更好:
:root {
--fixed-width: 220px;
--gap: 20px;
}
body {
display: flow-root; /*清除浮动*/
}
aside {
float: left;
width: var(--fixed-width);
}
main {
float: left;
margin-left: var(--gap);
width: calc(100% - var(--fixed-width) - var(--gap));
}
如果效果要更完美一点,可以考虑在 body
上添加一个 min-width
的设置。
在这个示例中,用到了两个关键点,第一个 CSS 函数中的 calc()
函数的功能,它可以做四则运算,第二个就是 CSS 值中的 %
,当百分比%
运用于 width
时,它的计算是相对于其父容器的 width
来计算。
Flexbox方案
Flexbox 方案就更简单了:
body {
display: flex;
gap: var(--gap)
}
aside {
width: var(--fixed-width);
}
main {
flex: 1;
}
里使用了 CSS Flexbox 布局,很简单。将body
声明为一个 Flexbox 容器(使用 display
) ,在侧边栏设置固定宽度,主列使用 flex:1
,他会扩展Flexbox容器的剩余空间。如果只是要现这样的一个效果,到此就可以了。
Grid 方案
Grid 方案和 Flexbox 很相似:
body {
display: grid;
gap: var(--gap);
grid-template-columns: var(--fixed-width) 1fr;
}
使用Grid 是最简单的!不过要掌握 Grid 就不简单了。这里关键是使用 grid-template-columns
定义了一个两列网格,其中第一列列宽是 --fixed-width
,第二列列宽是 1fr
。这个 fr
是 Grid 中独有的特性。
fr
单位代表网格容器中可用空间的一等份。
从这个描述中不难发现,在CSS网格布局中使用fr
单位确定尺寸的网格轨道被称为 弹性网格轨道 ,因为它们会根据网格容器剩余空间进行对网格轨道进行弹性缩放,这个有点类似于 Flexbox 布局中的 flex
有点类似。
一般情况之下,网格轨道使用fr
单位时,网格容器的可空间会按下面的公式来计算网格轨道尺寸:

对于fr
设置弹性网格轨道理解起来还是有一定的困惑,不过我们可以将其和%
结合起来,会更易于理解。简单地说:
1
个fr
(即1fr
)就是100%
网格容器可用空间;2
个fr
(即2fr
)是各50%
网格容器可用空间,即1fr
是50%
网格容器可用空间。以此类似,要是你有25
个fr
(即25fr
),那么每个fr
(1fr
)就是1/25
或4%
。
使用饼图可以很形象的描述fr
:

注意,一个饼图(圆)就相当于网格容器的可用空间,分割的份数就相当于设置了弹性系数的网格轨道。
上面三个方案效果都是一致的