本文期待
- 整合布局知识网络。
- 整合最佳实现并整理样式片段(以 less 为例),拿走即用。
- 高频面试题梳理回答。
知识网络
常见布局对于 CSS 的布局,在应用层面,分为三类,居中布局、多列布局、全屏布局;
- 居中分为垂直、水平和平面。
- 多列分为两列、三列、圣杯、双飞翼。
- 全屏分为等分、等高、全屏。
i
graph TB
A[常见布局] --> B1[居中布局]
B1 --五种方案--> C11[水平居中]
B1 --五种方案--> C12[垂直居中]
B1 --五种方案--> C13[平面居中]
A --> B3[全屏布局]
A --> B2[多列布局]
B2 --七种方案--> C21[两列右侧自适应布局]
B2 --四种方案--> C22[三列右侧自适应布局]
B2 --两种方案--> C23[三列中间自适应布局,圣杯-双飞翼]
每个布局类型都有他们对应的最佳实践,衡量标准主要是兼容性和组成场景,举栅格多列布局而言,移动端通用是 flex 新特性,而 PC 端则要更多的考虑兼容从而使用 margin 负值+盒模型 inline-block 技巧。
常见实现
这些布局的实现有六种常用实现:盒模型、浮动、定位、flex 布局、Grid 布局和 Shapes 布局。
graph TB
B4[常见实现] --> C1[Float]
B4 --> C2[盒模型]
B4 --> C3[定位]
B4 --> C4[Flex 和 grid]
B4 --> C5[Columns 和 Shapes]
接下来,我们以应用层面为分析主线,梳理分别的最佳实现吧!
居中布局
使用
复制粘贴
链接:https://github.com/Sympath/duojiUI/blob/master/src/packages/less/center.less。
垂直
需求可以分为四大类六种场景,从而衍生出对应的六种最佳处理。
graph TB
A[垂直居中] --> B1[内联场景]
A --> B2[块元素场景]
A --> B3[多元素场景]
A --> B4[脱离文档流场景]
B4 --> C1[定高]
B4 --> C2[不定高]
B4 --> C3[包裹性]
内联(inline)级别场景
采用父容器设置line-height即可。
// 内联(inline)级别 父容器设置
.inline-mixin (@line-height) {
line-height: @line-height;
}
块(block)级别场景
采用子元素转为inline-block然后复用内联场景即可。
// 块(block)级别 父容器设置
.block-mixin (@line-height) {
.center-y-inline(@line-height);
.item {
display: inline-block;
}
}
多子容器场景
适合 flex 布局, 父容器设置。
// flex 布局级别+多子容器 父容器设置
.flex {
display: flex;
justify-content: center;
}
脱离文档流
这些类均需要自身设置,包含两种情况,定高、不定高,不定奥存在两种解决方案,transform 和包裹性 margin。
// 定位级别+脱离文档流+不定高
.position {
position: absolute;
top: 50%;
transform: translateY(-50%); // 存在多浏览器兼容性问题,所以需要写多套
}
// 定位级别+脱离文档流+定高 自身设置
.position-mixin (@height : 100%) {
position: absolute;
top: 50%;
margin-top: -@height/2;
}
// 定位级别+脱离文档流+包裹性 (最佳方案) 自身设置
.position-wrap {
position: absolute;
top: 0;
bottom: 0;
margin: auto 0;
}
汇总记忆:汇总来说可以这么记忆,一共六种。
- 父级设置有三类,内联line-height,块级inline-block+line-height,多子容器flex+align-items: center。
- 自身设置有三类,脱离文档选定位,定高 margin-top 负值,不定高 translate,最佳包裹配置margin: auto。
水平
需求同样是这六种场景
graph TB
A[水平居中] --> B1[内联场景]
A --> B2[块元素场景]
A --> B3[多元素场景]
A --> B4[脱离文档流场景]
B4 --> C1[定高]
B4 --> C2[不定高]
B4 --> C3[包裹性]
内联(inline)级别场景
采用父容器设置line-height即可。
// 内联(inline)级别 父容器设置
.inline-mixin () {
text-align: center;
}
块(block)级别场景
采用子元素转为inline-block然后复用内联场景即可。
// 块(block)级别 自身设置
.block {
margin-left: auto;
margin-right: auto;
}
多子容器场景
适合 flex 布局, 父容器设置。
.flex {
display: flex;
align-items: center;
}
脱离文档流
这些类均需要自身设置,包含两种情况,定高、不定高,不定奥存在两种解决方案,transform 和包裹性 margin。
// 定位级别+脱离文档流+不定宽 自身设置
.position {
position: absolute;
left: 50%;
transform: translateX(-50%); // 存在多浏览器兼容性问题,所以需要写多套
}
// 定位级别+脱离文档流+定宽 自身设置
.position-mixin (@width:100%) {
position: absolute;
left: 50%;
margin-left: -@width/2;
}
// 定位级别+脱离文档流+包裹性 (最佳方案) 自身设置
.position-wrap {
position: absolute;
left: 0;
right: 0;
margin: 0 auto;
}
汇总记忆:汇总来说可以这么记忆,一共六种,父级设置有两类,内联text-align,多子容器display: flex+align-items: center;自身设置有 1+3,块级margin:auto;;脱离文档选定位,定宽 margin 负值,不定宽 translate,最佳包裹配置margin: auto。
平面
综合下水平和垂直就好。
#center {
.inline-mixin(@line-height) {
#center > #x > .inline;
#center > #y > .inline-mixin(@line-height)
}
// 块(block)级别 父容器设置+注意需要把子容器设置 item 的类名
.block-mixin (@line-height) {
.center-x-block;
.center-y-block(@line-height)
}
// flex 布局级别 多子容器 父容器设置
.flex {
#center > #x > .flex;
#center > #y > .flex;
}
// 定位级别 脱离文档流+定宽高 自身设置
.position-mixin (@width, @height) {
.center-x-position(@width);
.center-y-position(@height);
}
// 定位级别+脱离文档流+不定宽 自身设置
.position {
#center > #x > .position();
#center > #y > .position();
transform: translate(-50%, -50%);
}
// 定位级别+脱离文档流 自身设置 利用了包裹性(最佳方案)
.position-wrap {
#center > #x > .position-wrap();
#center > #y > .position-wrap();
margin: auto;
}
}
内容总结
综合如上我们可以得出一个超牛的代码模块,居中相关的拿走即用,这里说几条规定便于使用。
- 命名规范:如果需要传参,则定义成 mixin,命名后缀也需要带上这个,便于区分。
- 每个方案的使用说明都至少存在如下信息(面向场景 设置位置 特点(可无))。
#center {
//>>>>>>>>>>>>>>>>>> 垂直居中
#y {
// 内联(inline)级别 父容器设置
.inline-mixin (@line-height) {
line-height: @line-height;
}
// 块(block)级别 父容器设置
.block-mixin (@line-height) {
.center-y-inline(@line-height);
.item {
display: inline-block;
}
}
// flex 布局级别+多子容器 父容器设置
.flex {
display: flex;
align-items: center;
}
// 定位级别+脱离文档流+不定宽 自身设置
.position {
position: absolute;
top: 50%;
transform: translateY(-50%); // 存在多浏览器兼容性问题,所以需要写多套
}
// 定位级别+脱离文档流+定高 自身设置
.position-mixin (@height : 100%) {
position: absolute;
top: 50%;
margin-top: -@height/2;
}
// 定位级别+脱离文档流+包裹性 (最佳方案) 自身设置
.position-wrap {
position: absolute;
top: 0;
bottom: 0;
margin: auto 0;
}
}
//>>>>>>>>>>>>>>>>>> 水平居中
#x {
// 内联(inline)级别 父容器设置
.inline {
text-align: center;
}
// 块(block)级别 自身设置
.block {
margin-left: auto;
margin-right: auto;
}
// flex 布局级别+多子容器 父容器设置
.flex {
display: flex;
justify-content: center;
}
// 定位级别+脱离文档流+不定宽 自身设置
.position {
position: absolute;
left: 50%;
transform: translateX(-50%); // 存在多浏览器兼容性问题,所以需要写多套
}
// 定位级别+脱离文档流+定宽 自身设置
.position-mixin (@width:100%) {
position: absolute;
left: 50%;
margin-left: -@width/2;
}
// 定位级别+脱离文档流+包裹性 (最佳方案) 自身设置
.position-wrap {
position: absolute;
left: 0;
right: 0;
margin: 0 auto;
}
}
.inline-mixin(@line-height) {
#center > #x > .inline;
#center > #y > .inline-mixin(@line-height)
}
// 块(block)级别 父容器设置+注意需要把子容器设置 item 的类名
.block-mixin (@line-height) {
.center-x-block;
.center-y-block(@line-height)
}
// flex 布局级别 多子容器 父容器设置
.flex {
#center > #x > .flex;
#center > #y > .flex;
}
// 定位级别 脱离文档流+定宽高 自身设置
.position-mixin (@width, @height) {
.center-x-position(@width);
.center-y-position(@height);
}
// 定位级别+脱离文档流+不定宽 自身设置
.position {
#center > #x > .position();
#center > #y > .position();
transform: translate(-50%, -50%);
}
// 定位级别+脱离文档流 自身设置 利用了包裹性(最佳方案)
.position-wrap {
#center > #x > .position-wrap();
#center > #y > .position-wrap();
margin: auto;
}
}
多列布局之两列布局
在线预览 Demo:https://zzmwzy.gitee.io/css-test-online/dist/index.html。
需求:左列定宽,右列自适应。
Vue 版组件复制粘贴
链接:https://github.com/Sympath/duojiUI/blob/master/src/packages/layout/two-column-layout.vue。
存在七种解决方案,选出四种根据场景的最佳方案,另外三种扩展思路。
- 浮动:组合清浮动(非等高布局推荐)。
- 表格(等高-pc 端推荐)。
- flex 布局(等高-移动端推荐)。
- 定位(需要脱离文档流)。
- 浮动:组合 margin 负值。
- 浮动:组合 BFC(可以overflow或display: flex;都会有副作用,需要考虑场景使用)。
- gird 布局。
浮动:组合 margin 负值
结构:
<!-- 定宽 -->
<div id="left">div>
<!-- 自适应容器 -->
<div id="right">
<div id="inner">div>
div>
这种适合右侧比左侧高的情况,如果右侧比左侧高就凉了,因为浮动形成 bfc 从而会导致高度塌陷。
// 适合 PC 端 父容器 存在【右侧比左侧高,浮动导致塌陷】问题
.float-margin-mixin (@leftWidth: 0) {
.left {
float: left;
width: @leftWidth;
}
.right {
margin-left: @leftWidth;
}
}
浮动:组合清浮动
处理【右侧比左侧高,浮动导致塌陷】的情况。
1、改写 html 结构:在自适应模块外层进行包裹。
<div class="left">div>
<!-- 为自适应元素定位父级元素 -->
<div class="right-fix">
<div class="right">
<div class="inner">div>
div>
div>
2、样式设置:左右均浮动,然后在右侧外层下清除浮动。
// 浮动:组合清浮动
// 适合 PC 端 父容器 需要在右边自适应模块的 html 外层再包裹一层 right-fix 类名元素
.float-clear-mixin (@leftWidth: 0) {
.left,
.right-fix {
float: left;
}
.left {
width: @leftWidth;
}
.right {
}
}
浮动:组合 BFC
浮动元素不会和 BFC 重叠:
// 浮动:组合 BFC flex
// 适合 PC 端 父容器 均浮动(最佳方案)
.float-bfc-mixin (@leftWidth: 0) {
.left,
.right-fix {
float: left;
}
.left {
width: @leftWidth;
}
.right {
display: flex;
// 或 overflow: hidden;
}
}
表格
table 布局默认是等高布局:
// 表格
// 适合需要等高的布局 父容器 table 布局默认是等高布局
.table-mixin (@leftWidth: 0) {
display: table;
width: 100%;
.left {
display: table-cell;
width: @leftWidth;
}
.right {
display: table-cell;
}
}
定位
// 定位
// 适合需要脱离文档流的布局 父容器
.position-mixin (@leftWidth: 0) {
position: relative;
.left {
position: absolute;
width: @leftWidth;
}
.right {
position: absolute;
left: @leftWidth;
right: 0;
}
}
flex 布局
// flex 布局
// 适合不需要考虑兼容性的布局 父容器
.flex-mixin (@leftWidth: 0) {
display: flex;
.left {
width: @leftWidth;
}
.right {
flex: 1;
}
}
gird 布局
// gird 布局
// 适合不需要考虑兼容性的布局(目前更建议使用 flex) 父容器
.gird-mixin (@leftWidth: 0) {
display: grid;
grid-template-columns: @leftWidth auto; // 列宽
grid-template-rows: repeat(2, 600px); // 列高
}
两列布局整理汇总
// ## 两列布局
#two-column {
// 浮动:组合 margin 负值
// 适合 PC 端 父容器 存在【右侧比左侧高,浮动导致塌陷】问题
.float-margin-mixin (@leftWidth: 0) {
.left {
float: left;
width: @leftWidth;
}
.right {
margin-left: @leftWidth;
}
}
// 浮动:组合清浮动
// 适合 PC 端 父容器 需要在右边自适应模块的 html 外层再包裹一层 right-fix 类名元素
.float-clear-mixin (@leftWidth: 0) {
.left,
.right-fix {
float: left;
}
.left {
width: @leftWidth;
}
.right {
}
}
// 浮动:组合 BFC flex
// 适合 PC 端 父容器 均浮动(最佳方案)
.float-bfc-mixin (@leftWidth: 0) {
.left,
.right-fix {
float: left;
}
.left {
width: @leftWidth;
}
.right {
display: flex;
// 或 overflow: hidden;
}
}
// 表格
// 适合需要等高的布局 父容器 table 布局默认是等高布局
.table-mixin (@leftWidth: 0) {
display: table;
width: 100%;
.left {
display: table-cell;
width: @leftWidth;
}
.right {
display: table-cell;
}
}
// 定位
// 适合需要脱离文档流的布局 父容器
.position-mixin (@leftWidth: 0) {
position: relative;
.left {
position: absolute;
width: @leftWidth;
}
.right {
position: absolute;
left: @leftWidth;
right: 0;
}
}
// flex 布局
// 适合不需要考虑兼容性的布局 父容器
.flex-mixin (@leftWidth: 0) {
display: flex;
.left {
width: @leftWidth;
}
.right {
flex: 1;
}
}
// gird 布局
// 适合不需要考虑兼容性的布局(目前更建议使用 flex) 父容器
.gird-mixin (@leftWidth: 0) {
display: grid;
grid-template-columns: @leftWidth auto; // 列宽
grid-template-rows: repeat(2, 600px); // 列高
}
}
多列布局之三列布局
在线预览 Demo:https://zzmwzy.gitee.io/css-test-online/dist/index.html。
需求: 左中 定宽 右侧自适应。
Vue 版组件复制粘贴
链接:https://github.com/Sympath/duojiUI/blob/master/src/packages/layout/muti-column-layout.vue。
<div class="layout-wrapper" :class="mode">
<div class="left">
div>
<div class="center">
div>
<div class="right">
div>
div>
浮动:组合 margin
这种适合右侧比左侧高的情况,如果右侧比左侧高就凉了,因为浮动形成 bfc 从而会导致高度塌陷。
.layout-tree-column-float-margin (@leftWidth: 0) {
.left, .center{
float:left;
width: @leftWidth;
}
.right {
margin-left: @leftWidth;
}
}
浮动:组合 BFC
浮动元素不会和 BFC 重叠:
.layout-tree-column-float-bfc (@leftWidth) {
.left, .center {
float:left;
width: @leftWidth;
}
.right {
overflow: hidden;
}
}
表格
table 布局默认是等高布局:
.layout-tree-column-table (@leftWidth) {
display: table;
width: 100%;
.left, .center {
display: table-cell;
width: @leftWidth
}
.right {
display: table-cell;
}
}
定位
.layout-tree-column-position (@leftWidth) {
position: relative;
.left, .center {
width: @leftWidth
}
.right {
position: absolute;
left: @leftWidth;
right: 0;
}
}
flex 布局
.layout-tree-column-flex (@leftWidth) {
display:flex;
.left, .center {
width: @leftWidth
}
.right {
flex:1;
}
}
gird 布局
.layout-two-column-gird (@leftWidth, @centerWidth, @leftHeight) {
display: grid;
grid-template-columns: @leftWidth @centerWidth auto; // 列宽
grid-template-rows: repeat(2,@leftHeight); // 列高
.left {
}
.right {
}
}
三列布局整理汇总
#layout {
// ## 两列布局
#three-column {
// 浮动:组合 margin 负值
// 适合 PC 端 父容器 存在【右侧比左侧高,浮动导致塌陷】问题
.float-margin-mixin (@leftWidth) {
.left {
float:left;
width: @leftWidth;
}
.right {
margin-left: @leftWidth;
}
}
// 浮动:组合清浮动
// 适合 PC 端 父容器 需要在右边自适应模块的 html 外层再包裹一层 right-fix 类名元素
.float-clear-mixin (@leftWidth) {
.left, .right-fix {
float:left;
}
.left {
width: @leftWidth;
}
.right {
.inner {
clear: both;
}
}
}
// 浮动:组合 BFC
// 适合 PC 端 父容器 均浮动(最佳方案)
.float-bfc-mixin (@leftWidth) {
.left, .right-fix {
float:left;
}
.left {
width: @leftWidth;
}
.right {
.inner {
clear: both;
}
}
}
// 表格
// 适合需要等高的布局 父容器 table 布局默认是等高布局
.table-mixin (@leftWidth) {
display: table;
width: 100%;
.left {
display: table-cell;
width: @leftWidth
}
.right {
display: table-cell;
}
}
// 定位
// 适合需要脱离文档流的布局 父容器
.position-mixin (@leftWidth) {
position: relative;
.left {
width: @leftWidth
}
.right {
position: absolute;
left: @leftWidth;
right: 0;
}
}
// flex 布局
// 适合不需要考虑兼容性的布局 父容器
.flex-mixin (@leftWidth) {
display:flex;
.left {
width: @leftWidth
}
.right {
flex:1;
}
}
// gird 布局
// 适合不需要考虑兼容性的布局(目前更建议使用 flex) 父容器
.gird-mixin (@leftWidth) {
display: grid;
grid-template-columns: @leftWidth auto; // 列宽
grid-template-rows: repeat(2,600px); // 列高
.left {
}
.right {
}
}
}
}
多列布局之多列布局
在线预览 Demo:https://zzmwzy.gitee.io/css-test-online/dist/index.html。
需求: 两列定宽中间自适应。
Vue 版组件复制粘贴
链接:https://github.com/Sympath/duojiUI/blob/master/src/packages/layout/three-column-layout.vue。
圣杯布局
结构
注意:这里把 center 放在第一位,是为了优先加载。
<div class="parent">
<div class="center">中间div>
<div class="left">左边div>
<div class="right">右边div>
div>
- 水平排列:三个容器同时浮动,此时会引起左右侧内容掉落。
- 腾出位置:父容器加内填充,准备左右侧放置空间。
- 摆放位置:margin 负值实现同行排列,相对定位实现位置移动。
#layout {
// ## 三列布局
#three-column {
.holy-grail(@leftWeight, @rightWeight){
#center,#left,#right{float:left}
#parent{
padding-left: @leftWeight;
padding-right: @rightWeight;
}
#left{margin-left:-100%;position: relative;left:-@leftWeight;}
#right{margin-left:-@rightWeight;position: relative;right:-@rightWeight;}
}
}
}
双飞翼布局
结构
中间部分内部增加一层结构。
<div class="parent">
<div class="center">
<div class="inner">中间div>
div>
<div class="left">左边div>
<div class="right">右边div>
div>
- 水平排列:三个容器同时浮动,此时会引起左右侧内容掉落。
- 腾出位置:中间的容器的子容器设置 margin 外间距。
- 摆放位置:margin 负值实现。
#layout {
// ## 三列布局
#three-column {
.threesome-wing(@leftWeight, @rightWeight){
#center,#left,#right{float:left}
#inner{
margin-left:@leftWeight;
margin-right:@rightWeight;
}
#left{margin-left:-100%}
#right{margin-left:-@rightWeight;}
}
}
}
总结: 双飞翼把圣杯的定位位移的属性给优化掉了 margin + padding。
等分布局
浮动布局
html 结构:这里特殊一点,外层需要加一层 div。
<!-- 作为父级容器 -->
<div id="parent" class="clearfix">
<div><div class="col1">div>div>
<div><div class="col2">div>div>
<div><div class="col3">div>div>
<div><div class="col4">div>div>
<div><div class="col5">div>div>
div>
浮动+盒模型+margin 负值
- 水平排列:浮动实现水平排列。
- 等分:设置宽度。
- 间距:盒模型box-sizing: border-box + padding-left实现,设置在外层上,避免内容被影响。
- 最左侧间隙:父容器 margin 负值,这里不能对第一个元素特殊处理,会导致这个元素内容偏大。
// @numberOfRow 一行个数
// @space 元素间隙
.float(@numberOfRow, @space: 0) {
.parent{
margin-left: -@space;
}
.parent>div{
float: left;
width:1/@numberOfRow;
box-sizing: border-box;
padding-left: @space;
}
}
表格布局
html 结构:这里特殊一点,外层需要加一层 div。
<!-- 作为父级容器 -->
<div class="wrap">
<div id="parent" class="clearfix">
<div><div class="col1">div>div>
<div><div class="col2">div>div>
<div><div class="col3">div>div>
<div><div class="col4">div>div>
<div><div class="col5">div>div>
div>
div>
表格+margin 负值。
- 水平排列:表格布局实现水平排列,注意表格必须设置宽度 100%,不然不会继承父容器宽度。
- 等分:默认等分等高。
- 间距:border实现。
- 最左侧间隙:margin 负值,但这里不能对父容器处理,而是要在父容器外面再加一层,因为父容器身上设置了宽度 100%。
// @space 元素间隙
.table(@space: 0) {
.wrap {
margin-left: -@space;
}
.parent{
display: table;
width: 100%;
}
.parent>div{
display: table-cell;
border-left: @space solid #fff;
}
}
弹性布局
html 结构
<div id="parent">
<div class="col1">
div>
<div class="col2">
div>
<div class="col3">
div>
<div class="col4">
div>
div>
- 水平排列:弹性布局。
- 等分:父容器设置flex: 1即可。
- 间距:border实现。
- 最左侧间隙:父容器 margin 负值。
扩展:flex: 1是一个综合值,其值包含flex-grow、flex-shark和flex-basic,默认值为0 1 auto,即父多时子原样,父少时一起小,这个属性有继承性,所以可以放在父项目上进行统一设置。
// @space 元素间隙
.flex(@space: 0) {
.parent{
display: flex;
flex: 1;
margin-left: -@space;
}
.parent>div{
border-left: @space solid #fff;
}
}
最后汇总
至此,我们也就完成了前端知识中布局的梳理,知识网络的构建非一日之功,但成体系了,就成了正规军,加油!
本文布局均有在线案例,可见:https://zzmwzy.gitee.io/css-test-online/dist/index.html。
本文布局均实现对应的 vue 组件,拿走即用,其他框架可以类比自实现,仓库地址:https://github.com/Sympath/duojiUI/blob/master/src/packages/layout/。
本地组件发布至自己的组件仓库,可以去瞅瞅或点个 star,感谢!npm 地址:https://www.npmjs.com/package/duoji-ui。