布局和位置
布局和位置
到目前为止,以我们学过的知识,只能排列一条线,显然是无法满足我们正常的需要的。
要实现变化多端的页面效果,我们离不开布局。
一般旧式教程会从float
效果开始讲起,但目前现代浏览器会更建议使用flex和grid布局。
flex布局是一维的(线状),grid则是二维(平面,有x、y),目前大多数网站还是倾向于使用兼容性更好的flex布局。
flex布局
flex布局的标记存在容器(父标记)和元素(子标记)的区别。这很容易理解:
容器(默认情况下)横向方向称为主轴,与主轴垂直的轴称为交叉轴。元素会沿主轴逐一排列。

这里引用一下MDN:
MDN关于flex的说明
文档中采用了 flexbox 的区域就叫做 flex 容器。为了创建 flex 容器,我们把一个容器的 display 属性值改为 flex 或者 inline-flex。 完成这一步之后,容器中的直系子元素就会变为 flex 元素。 由于所有 CSS 属性都会有一个初始值,所以 flex 容器中的所有 flex 元素都会有下列行为:
- 元素排列为一行(flex-direction 属性的初始值是 row)。
- 元素从主轴的起始线开始。
- 元素不会在主维度方向拉伸,但是可以缩小。
- 元素被拉伸来填充交叉轴大小。
- flex-basis 属性为 auto。
- flex-wrap 属性为 nowrap。
涉及的属性
这里我们先列出所有涉及flex布局的属性,方便后面对照:
.container{ /* 用于容器(父标记)的属性 */
display: flex; /* 说明是个flex容器 */
flex-direction: ; /* 元素如何排列 */
flex-wrap: ; /* 元素是否允许换行 */
justify-content: ;/* 元素主轴上的对齐方式 */
align-items: ;/* 元素交叉轴上的对齐方式 */
}
.item{ /* 用于元素(子标记)的属性 */
flex-grow: ;/* 容器扩大时的增长规则 */
flex-shrink: ;/* 容器缩小时的收缩规则 */
flex-basis: ;/* 元素的尺寸,类似width*/
flex: ; /* 以上三者的简写法 */
align-self: ; /* 元素在交叉轴(竖直方向)上的对齐方式 */
order: ;
}
推荐的教程
这里列出B站比较推荐的关于flex的教程,突出一个2分钟掌握,作为概括性的了解比较适合:
还是要强调一下,不动手看了也没意义。
实战一下
我们假设要做这么一个效果,分为左右两栏,左边500px,右边是1000px,居中,中间用空白分开:

原始状态
以下内容请在编辑器里尝试一下,否则你记不住,注意!
现在假设有以下的两个块,我们希望进行分栏:
<div class="leftbar">
<h1>左边栏</h1>
</div>
<div class="rightbar">
<h1>右边栏</h1>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Vitae sit nihil numquam? Unde sed quam dolorum nisi? Repudiandae ipsum alias soluta molestiae! Iste ea saepe sapiente facere nemo eos natus?</p>
</div>
我们简单设置一下css,让页面不同的部分更醒目一些:
h1{
font-size: 3em;
}
div{
border: 2px solid black;
padding: 20px;
}
.rightbar>p{
font-size: 150%;
}
.leftbar{
margin-right: 20px;
}
.rightbar{
background-color: coral;
}
现在看看效果:

转为flex显示模式
第一步我们首先需要一个flex容器,然后把两个分栏放入容器中,并设置容器为display:flex
:
<div class="container">
<div class="leftbar"> ... </div>
<div class="rightbar"> ... </div>
<div>
.container{
display: flex;
}
这时你已经可以看到形成双栏了:

元素块的属性
不过别急,你可以拉动窗口,两个分栏的大小会随着窗口尺寸(父节点尺寸)的大小变化而自动调整。这种自动模式很可能蹦年满足你(或者客户)的要求。 例如左侧的侧边栏,显然太小了。我们可以通过flex-basis
来给出其大小:
.leftbar{
/* ... */
flex-basis: 500px;
}
.rightbar{
/* ... */
flex-basis: 1000px;
}
你会发现尺寸确实发生了变化,当窗口的尺寸超过1500时,两栏的尺寸符合我们的预期。不过如果此时你缩小窗口的尺寸,压缩到1500之内,两个栏的尺寸会同步缩小。
注意一点
flex是一种非常自动化的布局方式。好处是开箱即用,坏处是要精确调整需要非常多的配置。
这里就要理解元素的flex-grow
和flex-shrink
的作用。
我们为.rightbar
设置flex-grow:
.rightbar{
/* ... */
flex-grow: 1;
}
此时你会发现,在窗口尺寸放大时,右侧块也会伴随变大。
为.leftbar
设置flex-shrink
:
.leftbar{
/* ... */
flex-shrink: 0;
}
你会发现当窗口缩小时,左侧块并不会同步缩小,而是保持原来的尺寸。
块尺寸到底时如何计算的?
这里的计算逻辑略有点绕,但并不难理解。
你可能会注意到对于flex-basis
属性,我们设置的时候有px
单位,但flex-grow
和flex-shrink
里并没有单位——对,这里设置的实际上是比例。
我们可以这样理解:对于flex容器内部的元素,他总是倾向于完全填满整个容器(回忆一下,最初没有配置时,是不是这样的)。
如果容器的尺寸并不等于内部元素的尺寸之和时,内部元素就需要放大或者缩小来保证正好填满整个容器。
那么问题就是,放大或者缩小的空间要如何分配。
举例来说,如果容器内有3个元素,每个元素的flex-basis
都是200px
,总体要占用600px
的空间。
如果容器尺寸是840px
,那么有840px-600px=240px
的空间需要填充。flex-grow
就给出了分配这240px
的比例。
假如我们为每个块都设置flex-grow:1
,那么显然他就会被平均分配给三个块,每个块增加80px
,也就是每个块都变成680px
。
同理,如果我们为中间的块单独设置flex-grow:2
,两边的块维持1
,那么两边两个块分60px
,中间的块分120px
。
对于flex-shrink
也是类似的,只是分配的变成了每个块要负责缩小多少尺寸。
权威的解释可以查手册:
最后flex
可以对以上三者进行简写,依次按flex-grow
、flex-shrink
和flex-basis
的顺序排列即可。
例如:flex : 1 0 300px
相当于:
flex-grow : 1;
flex-shrink : 0;
flex-basis : 300px;
三条规则。
修饰一下
针对我们上面的要求,我们可以为.leftbar
和.rightbar
加上flex-shrink : 0
,并为他们设置不同的flex-basis
:
.leftbar{
background-color: aqua;
flex-basis: 500px;
flex-shrink: 0;
}
.rightbar{
background-color: coral;
flex-basis: 1000px;
flex-shrink: 0;
}
此时缩放页面将不再影响两个块的尺寸了。
决定元素如何(横向)排列的属性是容器的justify-content
属性。设置为center
则可以将元素横向居中放置。 这个属性要设置在容器、也就是.container
上:
.container{
display: flex;
justify-content: center;
}
就可以实现居中显示了:

最后我们为两栏设置margin
来为两栏创建一个空间来分隔:
.leftbar{
background-color: aqua;
flex-basis: 500px;
flex-shrink: 0;
margin-right: 20px;
}
.rightbar{
background-color: coral;
flex-basis: 1000px;
flex-shrink: 0;
margin-left: 20px;
}
就可以得到我们想要的结果了。
其他资源
其他相关的属性,可以查看上面的手册、教程和视频并自己研究。
再次强调:学习主体是学生,不是老师!
MDN页面上有两个很好的学习资源:
- A Complete Guide to Flexbox,这是一篇相对比较完善的(英语)教程。
- 青蛙游戏,一个非常有趣的页面游戏,通过flex布局把青蛙放到正确的位置上。妥妥寓教于乐。国内有人做了汉化。
定位模式
绝对定位
你一定经常遇到一些覆盖在页面上怎么都点不掉的图片。或者一些在页面上飘来飘去的内容。这一般是应用了绝对定位。
绝对定位的设置
我们页面最下方增加一个div
并配置对应的css:
<div class="absolute">
<h1>
绝对定位
</h1>
</div>
.absolute{
width: 200px;
background-color: yellow;
position:absolute;
}
这个div
会显示在页面的下方,与正常的页面并没有太大区别。
我们还需要为标记设置位置信息,才能放置到我们希望的位置:
.absolute{
width: 200px;
background-color: yellow;
position:absolute;
left: 100px;
top: 100px;
}

你可以尝试调整left
和top
对应的值来移动该标记。
z-index属性
你可能会想到,假如有多个绝对定位的块,他们之间可能相互覆盖,那么这种覆盖次序可以被控制吗?

更进一步,我们知道正常的页面内容会被absolute块覆盖,那么能否反过来呢?
确实是可以的——利用块的z-index
属性。所谓z
轴,可以视为一个垂直于屏幕向外(朝向观众)的轴,z
值越高,就放置于越接近观众的位置。
也就是说,z
值高的标记会覆盖z
值低的标记。
而正常的页面块处于z=0
的平面,也就是说,z
值小于0的块会被正常页面遮挡。
试试
自己调整一下块的z-index
,看看覆盖效果。
固定定位
绝对定位的位置是相对页面内容的定位。固定定位则是相对窗口的定位,这两者之间相差一个滚动条。
简单说,如果页面滚动,块会同步移动,那么是绝对定位,否则是相对定位。
这种定位通常可以应用于一些希望不管页面如何滚动都会出现的块,例如:返回顶部的按钮,或者页面的目录等等。
试试
把之前的absolute块改成fix的再试试看,注意要充分填充页面内容,保证页面有滚动空间。
你发现了吗
本页面是不是也有类似的块呢?那么他真的是固定定位的吗?
怎么查看?
相对定位
有绝对定位就有相对定位,相对定位可以看作是在本地位置的微调,下面的例子就非常清楚:
<div>
<h1>移动这个<span class="relative">块</span></h1>
</div>
.relative{
color: red;
position: relative;
left: -20px;
top: 20px;
border: 2px solid black;
}
点击右侧的jsfiddle
或codepen
按钮可以进入测试页面自行测试。
其他布局方式
在flex布局(从css3才开始支持)之前。人们使用浮动布局来实现分栏,这种布局涉及到相对复杂的页面流的理解,相对难以理解。
你可以查看这里来获得完整的理解。
不得不承认,有些布局确实flex是无能为力的,例如这个:

但实际上这种需求并不多,并且浮动布局极大地增加了记忆和理解的负担,并需要更多的调试。