前言
开发每张网页都离不开布局,基于良好布局打下基础,才能使后续的开发更顺利。当然不能停留在IExplorer时代那种局限思维中,没办法解决的布局都用JS实现。今时不同往日,现代属性能更好地快速实现各种布局,节约更多时间去摸鱼。
布局
为了方便记忆,我根据属性聚合度将跟布局有关系的属性分类并划分为以下基本布局。
- 普通布局:
display:block/inline - 浮动布局:
float:left/right - 定位布局:
position:relative/absolute/fixed、left/right/top/bottom/z-index - 表格布局:
table系列属性 - 弹性布局:
display:flex/inline-flex、flex系列属性 - 多列布局:
column系列属性 - 格栅布局:
display:grid/inline-grid、grid系列属性 - 响应式布局:
em/rem/vw/vh/vmin/vmax、媒体查询
众多跟布局相关属性到底要如何结合才能完成想要的布局?开发时具体使用何种属性更为适合?这些都是布局方式中必须面对的问题。本章着重从常见布局技巧说起,怎样的属性搭配才能玩转网页排版。
在8种基本布局中,我还是推荐浮动布局、定位布局和弹性布局,熟悉这三种布局基本上能解决很多网页排版问题。表格布局尽量不要使用,在第3章有提到,可能很小的一个改动就会造成整个<table>回流;格栅布局其实是一个很不错的布局方式,无奈兼容性不是很好,所以我较少研究,后续兼容性上来了我会更新本章格栅布局相关内容。
弹性布局是一个好东西,完全掌握后能创造出很多意想不到的事情。玩转CSS神操作骚技巧离不开布局方式,更离不开弹性布局。若未接触过弹性布局相关属性的同学,可自行百度,网上一搜一大把就感觉无必要在此详细讲述了,贴上一篇还不错的的教程《深度解析CSS弹性布局》与《48张小图带你领略Flex布局之美》。若不能理解,推荐一个可视化Flex布局的网站Flexbox,同步查看相关属性的表现状态。相信浏览这些网站也能快速学习到弹性布局的好玩之处。
清除浮动
在各种经典布局方式中,可能会结合浮动布局相关属性。使用float会使节点脱流导致父节点高度坍塌,若不对父节点显式声明高度则很有必要给父节点清除浮动。定义以下clearfix用于清除浮动,给父节点添加clearfix类名即可。值得注意,clearfix已占用::after,所以使用clearfix的父节点就不能再声明::after了,可改用::before。
.clearfix::after {
display: block;
visibility: hidden;
clear: both;
height: 0;
font-size: 0;
content: "";
}
我就不详细讲述清除浮动的原理与分析了,有兴趣的同学请查看Clearfix。
全屏布局
经典的全屏布局由顶部、底部和主体三部分组成,其特性为三部分左右满屏拉伸、顶部底部高度固定和主体高度自适应。该布局很常见,也是很多Web应用主体的主流布局。通常使用<header>、<footer>和<main>三个标签语义化排版,<main>内还可加入<aside>侧栏或其他语义化标签。
<div class="fullscreen-layout">
<header></header>
<main></main>
<footer></footer>
</div>
position + left/right/top/bottom
三部分统一声明left:0与right:0将其左右满屏拉伸;顶部与底部分别声明top:0与bottom:0将其吸顶与吸底,声明俩高度为固定值;将主体的top与bottom分别声明为顶部高度与底部高度。通过绝对定位的方式将三部分固定在特定位置,使其互不影响。
移动端基本都是以该布局为主,不信打开任意一款App瞧瞧。实现起来较简单,基于其左右满屏拉伸的特性下手即可。
.fullscreen-layout {
position: relative;
width: 400px;
height: 400px;
header,
footer,
main {
position: absolute;
left: 0;
right: 0;
}
header {
top: 0;
height: 50px;
background-color: #f66;
}
footer {
bottom: 0;
height: 50px;
background-color: #66f;
}
main {
top: 50px;
bottom: 50px;
background-color: #3c9;
}
}
flex
使用flex实现会更简洁。display:flex默认让子节点横向排列,需声明flex-direction:column改变子节点排列方向为纵向排列;顶部与底部高度固定,主体声明flex:1自适应高度。
.fullscreen-layout {
display: flex;
flex-direction: column;
width: 400px;
height: 400px;
header {
height: 50px;
background-color: #f66;
}
footer {
height: 50px;
background-color: #66f;
}
main {
flex: 1;
background-color: #3c9;
}
}
若<main>需表现为可滚动状态,千万不要声明overflow:auto让容器自适应滚动,这样做有可能因为其他格式化上下文的影响而导致自适应滚动失效或产生其他未知效果。解决方案是在<main>中插入一个<div>并声明如下。
.fullscreen-layout {
main {
overflow: hidden;
div {
overflow: auto;
height: 100%;
}
}
}
两列布局
经典的两列布局由左右两列组成,其特性为一列宽度固定、另一列宽度自适应和两列高度固定且相等。以下以左列宽度固定与右列宽度自适应为例,反之同理。
<div class="two-column-layout">
<div class="left"></div>
<div class="right"></div>
</div>
float + margin-left/right
左列声明float:left与固定宽度,因为float使节点脱流,右列需声明margin-left为左列宽度,以保证两列不会重叠。
.two-column-layout {
width: 400px;
height: 400px;
.left {
float: left;
width: 100px;
height: 100%;
background-color: #f66;
}
.right {
margin-left: 100px;
height: 100%;
background-color: #66f;
}
}
overflow + float
左列声明同上,右列声明overflow:hidden使其形成BFC区域与外界隔离。
.two-column-layout {
width: 400px;
height: 400px;
.left {
float: left;
width: 100px;
height: 100%;
background-color: #f66;
}
.right {
overflow: hidden;
height: 100%;
background-color: #66f;
}
}
flex
使用flex实现会更简洁。左列声明固定宽度,右列声明flex:1自适应宽度。
.two-column-layout {
display: flex;
width: 400px;
height: 400px;
.left {
width: 100px;
background-color: #f66;
}
.right {
flex: 1;
background-color: #66f;
}
}
三列布局
经典的三列布局由左中右三列组成,其特性为连续两列宽度固定、剩余一列宽度自适应和三列高度固定且相等。以下以左中列宽度固定与右列宽度自适应为例,反之同理。整体的实现原理与上述两列布局一样。
<div class="three-column-layout">
<div class="left"></div>
<div class="center"></div>
<div class="right"></div>
</div>
为了让右列宽度自适应计算,就不使用float + margin-left的方式了,若使用margin-left还得结合左中列宽度计算。
overflow + float
左列与中列声明float:left与固定宽度,右列声明flex:1自适应宽度。
.three-column-layout {
width: 400px;
height: 400px;
.left {
float: left;
width: 50px;
height: 100%;
background-color: #f66;
}
.center {
float: left;
width: 100px;
height: 100%;
background-color: #66f;
}
.right {
overflow: hidden;
height: 100%;
background-color: #3c9;
}
}
flex
使用flex实现会更简洁。还是flex大法好。
.three-column-layout {
display: flex;
width: 400px;
height: 400px;
.left {
width: 50px;
background-color: #f66;
}
.center {
width: 100px;
background-color: #66f;
}
.right {
flex: 1;
background-color: #3c9;
}
}
圣杯布局/双飞翼布局
经典的圣杯布局与双飞翼布局都是由左中右三列组成,其特性为左右两列宽度固定、中间一列宽度自适应和三列高度固定且相等。其实也是上述两列布局与三列布局的变体,整体的实现原理与上述N列布局一样,可能就是一些细节需注意。
圣杯布局与双飞翼布局大体相同但也存在一点不同,区别在于双飞翼布局中列需加入一个子节点。在常规实现方式中也是在中列中做文章:如何使中列内容不被左右列遮挡。
- 相同
- 中列放首位且声明其宽高占满父节点
- 被挤出的左右列使用
float与margin负值将其拉回与中列处在同一水平线中
- 不同
- 圣杯布局:父节点声明
padding为左右列留出空位,将左右列固定在空位中 - 双飞翼布局:中列加入子节点并声明
margin为左右列让出空位,将左右列固定在空位中
- 圣杯布局:父节点声明
圣杯布局:float + margin-left/right + padding-left/right
因为浮动节点在位置中不能高于前面或平级的非浮动节点,否则会导致浮动节点下沉,因此在编写HTML结构时,将中列节点挪到右列节点后面。
左列与右列声明float浮动起来并通过声明margin负值将其拉回与中列处在同一水平线中。
<div class="grail-layout">
<div class="left"></div>
<div class="right"></div>
<div class="center"></div>
</div>
.grail-layout {
padding: 0 100px;
width: 400px;
height: 400px;
.left {
float: left;
margin-left: -100px;
width: 100px;
height: 100%;
background-color: #f66;
}
.right {
float: right;
margin-right: -100px;
width: 100px;
height: 100%;
background-color: #66f;
}
.center {
height: 100%;
background-color: #3c9;
}
}
双飞翼布局:float + margin-left/right
HTML结构大体同上,只在中列加入一个子节点<div>。根据两者区别,CSS声明会与上述圣杯布局有一点点出入,可观察对比找出不同地方。
<div class="grail-layout">
<div class="left"></div>
<div class="right"></div>
<div class="center">
<div></div>
</div>
</div>
.grail-layout {
width: 400px;
height: 400px;
.left {
float: left;
width: 100px;
height: 100%;
background-color: #f66;
}
.right {
float: right;
width: 100px;
height: 100%;
background-color: #66f;
}
.center {
margin: 0 100px;
height: 100%;
background-color: #3c9;
}
}
圣杯布局/双飞翼布局:flex
使用flex实现圣杯布局/双飞翼布局可忽略上述分析,左右两列宽度固定,中列宽度自适应。
<div class="grail-layout">
<div class="left"></div>
<div class="center"></div>
<div class="right"></div>
</div>
.grail-layout {
display: flex;
width: 400px;
height: 400px;
.left {
width: 100px;
background-color: #f66;
}
.center {
flex: 1;
background-color: #3c9;
}
.right {
width: 100px;
background-color: #66f;
}
}
均分布局
经典的均分布局由多列组成,其特性为每列宽度相等与每列高度固定且相等。总体来说也是最简单的经典布局,因为每列宽度相等,所以很易找到适合的方式处理。
<div class="average-layout">
<div class="one"></div>
<div class="two"></div>
<div class="three"></div>
<div class="four"></div>
</div>
.one {
background-color: #f66;
}
.two {
background-color: #66f;
}
.three {
background-color: #f90;
}
.four {
background-color: #09f;
}
float + width
每列宽度声明为相等的百分比,若有四列则声明width:25%。N列就用公式100 / n求出最终百分比宽度,记得保留2位小数,懒人还可用width:calc(100% / n)自动计算呢!
.average-layout {
width: 400px;
height: 400px;
div {
float: left;
width: 25%;
height: 100%;
}
}
column
使用column实现会让代码语义化更明确。column相关属性为列排版应运而生,相对flex相关属性来说更易懂易学。
父节点声明column-count:4把子节点划分为均等的四列,声明column-gap:0使子节点的间距为零。
.average-layout {
column-count: 4;
column-gap: 0;
width: 400px;
height: 400px;
div {
height: 100%;
}
}
flex
使用flex实现会更简洁。节点声明display:flex生成FFC容器,此时所有子节点的高度都相等。因为容器的align-items默认为stretch,所有子节点将占满整个容器的高度。每列声明flex:1自适应宽度。
.average-layout {
display: flex;
width: 400px;
height: 400px;
div {
flex: 1;
}
}
居中布局
居中布局不管在开发还是面试,都是一个出现频率很高的场景。很多同学可能都会死记硬背,若根据不同场景使用不同居中布局,那死记硬背也不一定帮得上忙,所以剖析其原理与技巧再自由组合,相信能开发出更多的使用方式,当然死记硬背也不会存在了。
以下是我总结的水平居中与垂直居中的实现方式,分开了解水平居中与垂直居中的原理,是玩转居中布局的最重要步骤。
水平居中
- margin:0 auto + width:fit-content:应用于
全部元素 - 块级元素 + margin:0 auto + width:应用于
块级元素- 若节点不是块级元素需声明
display:block - 若节点宽度已隐式声明则无需显式声明
width
- 若节点不是块级元素需声明
- 行内元素 + text-aligin:center:应用于
行内元素- 父节点声明
text-align - 若节点不是行内元素需声明
display:inline/inline-block
- 父节点声明
- position + left/right + margin-left/right + width:应用于
全部元素 - position + left/right + transform:translateX(-50%):应用于
全部元素 - display:flex + justify-content:center:应用于
全部元素的子节点
垂直居中
- 块级元素 + padding-top/bottom:应用于
块级元素- 父节点高度未声明或自适应
- 若节点不是块级元素需声明
display:block
- 行内元素 + line-height:应用于
行内元素- 父节点声明
line-height - 若节点不是行内元素需声明
display:inline/inline-block
- 父节点声明
- display:table + display:table-cell + vertical-align:middle:应用于
全部元素- 父节点声明
display:table - 子节点声明
display:table-cell与vertical-align:middle
- 父节点声明
- display:table-cell + vertical-align:middle:应用于
全部元素的子节点 - position + top/bottom + margin-top/bottom + height:应用于
全部元素 - position + top/bottom + transform:translateY(-50%):应用于
全部元素 - display:flex + align-items:center:应用于
全部元素的子节点 - display:flex + margin:auto 0:应用于
全部元素- 父节点中声明
display:flex - 子节点声明
margin:auto 0
- 父节点中声明
浏览器会为文本生成一个匿名行内盒,让文本参与IFC,所以可认为文本是行内元素,可回看第4章。
结合上述实现方式就能完成一些常见水平垂直居中布局,未出现的方式可在评论中补充,方便一起学习。注意注意,上述任何水平居中与垂直居中方式不是随意组合就能生效,需详细分析可行性。以下是一些组合成功的水平垂直居中布局。
假设节点是块级元素,意味着隐式声明display:block,例如以下的<div></div>,围绕着该<div>实现各种水平垂直居中布局。
<div class="center-layout">
<div></div>
</div>
.center-layout {
width: 400px;
height: 400px;
background-color: #f66;
div {
width: 100px;
height: 100px;
background-color: #66f;
}
}
display:inline-block
<div>声明display:inline-block将其变成行内块级元素,那可用text-align与line-height声明水平垂直居中了,但行内块级元素与匿名行内盒的基线对齐存在很大差异,所以需声明vertical-align:middle将其调整到垂直居中的位置,不过这也是近似垂直居中,父节点最后还需声明font-size:0消除该差异。
.center-layout {
line-height: 400px;
text-align: center;
font-size: 0;
div {
display: inline-block;
vertical-align: middle;
}
}
display:table-cell
父节点声明display:table-cell模拟表格布局的垂直居中;子节点声明margin:0 auto使其水平居中。
.center-layout {
display: table-cell;
vertical-align: middle;
div {
margin: 0 auto;
}
}
position
该方式也是最传统最稳定的水平垂直居中布局了,唯二的缺点就是声明属性稍多与必须已知宽高。要点是使用margin负值将节点拉回最中间,所以必须已知宽高才能计算margin负值,通常是margin-left与margin-top,可连写为margin:-(height/2) 0 0 -(width/2)。
.center-layout {
position: relative;
div {
position: absolute;
left: 50%;
top: 50%;
margin: -50px 0 0 -50px;
}
}
自从CSS3的transform普及后,声明transform:translate(-50%,-50%)可代替margin负值了,这样就无需声明宽高与计算宽高的二分之一是多少,真正做到自适应水平垂直居中。
这样处理会存在一个缺陷,若节点需额外使用transform,那就麻烦了。将额外的transform合并到水平垂直居中的transform:translate(-50%,-50%)中,就会存在一个较棘手的变换顺序问题,在第12章会详细讲述。解决方案就是在节点外部套上一层<div>,把transform:translate(-50%,-50%)转嫁到<div>中,那节点就能自由使用transform了。
.center-layout {
position: relative;
div {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
div {
transform: rotate(180deg);
}
}
}
flex
目前最强大的方式,无需多说,用惯flex都会知道。
.center-layout {
display: flex;
justify-content: center;
align-items: center;
}
当然还有一个隐藏的终极方式,也是史上最简方式。只需声明两个重要属性!父节点声明display:flex生成FFC容器,子节点声明margin:auto让浏览器自动计算子节点到父节点边上的距离。
.center-layout {
display: flex;
div {
margin: auto;
}
}
该CSS神操作骚技巧在后续会经常使用,大家期待下啦。
文字布局
文本环绕
利用float使节点脱流的原理实现,可回看第4章。
<div class="text-wrapping">
<img src="https://jowayyoung.github.io/static/img/icss/thor.jpg">
XXXXX......(很多个X)
</div>
.text-wrapping {
overflow: hidden;
width: 400px;
height: 300px;
font-size: 20px;
color: #f66;
word-break: break-all;
img {
float: left;
margin: 10px;
height: 200px;
}
}
文字溢出
嘿嘿,最常见单行文字溢出与多行问题溢出来啦。
<div class="bruce flex-ct-y" data-title="使用text-overflow控制溢出文本">
<p class="ellipsis-text s-line sl-ellipsis">CSS非常有趣与搞怪...</p>
<p class="ellipsis-text m-line ml-ellipsis">层叠样式表(CSS)是一种用于表现HTML...</p>
<p class="ellipsis-text m-line mls-ellipsis">层叠样式表(CSS)是一种用于表现HTML...</p>
</div>
.ellipsis-text {
line-height: 30px;
font-size: 20px;
&.s-line {
width: 200px;
}
&.m-line {
margin-top: 10px;
width: 400px;
text-align: justify;
}
}
单行文字溢出:overflow + text-overflow
.ellipsis-text {
&.sl-ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
多行文字溢出:flex + overflow + text-overflow
使用旧版弹性布局模拟多行文字溢出,只能在Webkit内核中使用,局限性太大了。
display:-webkit-box:将容器作为弹性伸缩盒模型-webkit-box-orient:弹性伸缩盒模型子节点的排列方式-webkit-line-clamp:限制容器最多显示多少行文本
.ellipsis-text {
&.ml-ellipsis {
display: -webkit-box;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
}
}
得通过一些兼容性稳定的属性模拟该溢出省略号,当然是使用伪元素::after胜任该工作了。结合max-height与line-height计算最大显示行数,通过定位布局把省略号定位到整段文字的右下角,使用linear-gradient()调整渐变背景颜色稍微润色下省略号使其看上去自然一些。
.ellipsis-text {
&.mls-ellipsis {
overflow: hidden;
position: relative;
max-height: 90px;
&::after {
position: absolute;
right: 0;
bottom: 0;
padding-left: 40px;
background: linear-gradient(to right, transparent, #fff 50%);
content: "...";
}
}
}
虽然该方式兼容性较好,但单行文字也会出现省略号,只能结合JS额外处理了。

.center {
margin: 0 100px;
height: 100%;
}
这里不是很理解,margin不是应该设置给内层的div吗。
嘿嘿,最常用的单行文字溢出和多行问题溢出来啦。
排版