由「两栏布局」引发的对「元素浮动/flex布局/绝对定位」 的思考
背景
笔者在工作中遇到不少管理平台类的项目,这些项目往往具有侧边栏加内容区这样的布局方式。
笔者遇到这样的需求时,往往通过团队选择的 UI组件库 的 Layout 组件实现效果,这样做的结果是: 需求快速响应了,但失去了锻炼 CSS 的机会。
本着吃透技术的理念,笔者调研了多种CSS实现两栏布局的方案,收获了多种布局知识,现分享出来,希望给掘友们一些启发💡。
两栏布局
需求
两栏布局顾名思义是有2个栏目,一个叫左边栏,一个叫右边栏,左边栏宽度固定为 100px, 右边栏宽度自适应,所谓自适应就是宽度不固定,它随着屏幕宽度的变化而变化,但始终填满这一行中除去左边栏的剩余空间。
初始代码如下:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>两栏布局</title>
<style>
.container {
background-color: yellowgreen;
}
.left {
width: 100px;
height: 100px;
background-color: lightcoral;
}
.right {
background-color: transparent;
}
</style>
</head>
<body>
<div class="container">
<div class="left">左边栏</div>
<div class="right">右边栏</div>
</div>
</body>
</html>
实现方案1
第 1 套方案利用浮动来实现。
左边栏向左浮动,右边栏设置外左边距为左边栏的宽度,这样左右布局就做好了,但是左边栏浮动使得父元素不能将左边栏的高度计算进来,造成了经典的高度塌陷问题。

这时候就需要清除浮动,清除浮动有多种方式,比如:
- 给父元素创建一个
BFC,如设置display: flow-root,overflow: hidden等,BFC元素的高度会把浮动子元素的高度计算进来。 - 增加一个末尾元素, 设置
clear:both。 - 给
父元素添加一个::after伪元素, 设置content:''; display:block; clear:both;。 - 给
父元素定义height属性,撑开高度不影响后面的元素。
这里笔者给父元素设置了一个 display: flow-root; 的样式,掘友们也可以尝试其他方法来实现,只要让父元素把左边栏的高度计算进来即可,这样就解决了高度塌陷问题。

CSS 代码
方案1最终的 CSS 代码如下:
.container {
/* 可换成其他清除浮动的方式 */
display: flow-root;
background-color: yellowgreen;
}
.left {
float: left;
width: 100px;
height: 100px;
background-color: lightcoral;
}
.right {
margin-left: 100px;
background-color: transparent;
}
实现方案2
第 2 套方案通过 flex 布局来实现。
父元素设置 display: flex 启用 flex 布局,默认 flex-direction 就是 row 的,笔者也把它写出来了(笔者习惯把默认样式写出来,为了更清晰~),这样左右边栏就会排成一行。
然后给右边栏设置 flex: 1,加上以后就实现了右边栏宽度自适应。但是这怎么理解?
flex: 1 是一条简写样式,完整的样式描述是 flex-grow: 1; flex-shrink: 1; flex-basis: 0;。
要理解这套样式,首先要理解什么是 flex 布局里的剩余空间:
根据MDN的描述,剩余空间是 flex 容器的大小减去所有 flex 项的大小加起来的大小。
理解 flex:1
现在可以来理解 flex:1 了,具体描述是:
flex-grow: 1
flex-grow 这个属性规定了 flex子项 在 flex容器 中分配剩余空间的相对比例。
具体到现在这个场景,由于子项中只有 右边栏 设置了 flex-grow,为 1,所以不需要和别的子项瓜分剩余空间,而是全部占有,正因如此 右边栏 实现了宽度自适应。
flex-shrink: 1
flex-shrink 属性指定了 flex子项 的收缩规则。flex子项 仅在默认宽度之和大于容器的时候才会发生收缩,这里不会发生右边栏宽度超过容器的情况,所以该属性无影响。
flex-basis: 0
flex-basis 属性指定了 flex子项 在主轴方向的初始大小,这里设置为 0 使得剩余空间就是 父元素 减去 左边栏 的宽度,所以 右边栏 能够完全充满剩余空间。
CSS 代码
方案2 最终的 CSS代码 如下:
.container {
display: flex;
flex-direction: row;
}
.left {
width: 100px;
height: 100px;
background-color: lightcoral;
}
.right {
flex-grow: 1;
flex-shrink: 1;
flex-basis: 0;
background-color: lightgreen;
}
实现方案3
第 3 套方案通过 相对/绝对定位 来实现。
首先父元素设置相对定位 position: relative;,然后让左边栏绝对定位 display: absolute,这样左右就排成一行了。
但是,左边栏因为绝对定位脱离了文档流,造成重叠了,左边栏压在右边栏之上:

这时,设置右边栏的外左边距为左边栏宽度,让右边栏被撑出来即可。
.right {
margin-left: 100px;
background-color: lightgreen;
}

但是,由于绝对定位,此时的父元素高度是不包含左边栏高度的,这时就得手动设置高度了(父元素和右边栏都要设置),因为这不是浮动产生的高度塌陷,而是左边栏完全脱离了文档流,不对父元素产生影响了。
.container {
height: 100px;
position: relative;
}
.right {
height: 100%;
margin-left: 100px;
background-color: lightgreen;
}
CSS 代码
方案3 最终的 CSS代码 如下:
.container {
height: 100px;
position: relative;
}
.left {
position: absolute;
width: 100px;
height: 100px;
background-color: lightcoral;
}
.right {
height: 100%;
margin-left: 100px;
background-color: lightgreen;
}
总结
本文介绍了「两栏布局」的 3 套解决方案,并简要探讨了每个方案遇到的一些 CSS 知识点,这种遇到问题发散地寻找知识的方式是非常好的学习方法,希望对掘友们有所启发。
(完)
转载自:https://juejin.cn/post/7196227432416264248