相信所有前端开发者的入门课,都是从 div 和 css 开始的,CSS 作为基础中的基础,除了让页面因为样式而变得丰富美观外,更是决定了页面元素的排列布局,然而很多时候,当我们对 CSS 一知半解,我们总会在开发中遇到元素不能按照我们所想的那样呈现出来的问题,所以本文就来系统的讲讲 CSS 的布局定位。
首先,我们来了解构成页面的元素。在 CSS 中,我们的每个元素本质上都是一个盒子,盒模型决定了一个元素的大小和他所占大小,盒模型由以下几部分组成:
那么元素的大小到底取决于什么呢?目前有两种盒模型:W3C 盒模型和IE 盒模型,两种盒模型的计算方法不同,在 CSS 中,我们可以用box-sizing指定使用哪种方式计算:
看个例子:
// W3C盒模型
<style>
.box {
width: 200px;
height: 200px;
padding: 10px;
margin: 10px;
border: 5px solid red;
background-color: yellowgreen;
}
</style>
<div class="box"></div>
// IE盒模型
<style>
.box {
width: 200px;
height: 200px;
padding: 10px;
margin: 10px;
border: 5px solid red;
background-color: yellowgreen;
box-sizing: border-box;
}
</style>
<div class="box"></div>
所以我们发现,不管是哪一种盒模型,margin 的长度都不会被包含在元素的宽高里,但是 magin 将决定元素在其父元素中的位置。
display用于定义建立布局时元素生成的显示框类型,会影响到兄弟元素之间的布局,他的取值有以下几种:
下面我们将对inline-block,flex,run-in这几个属性做详细讲解。
inline-block和float,flex并称实现元素行内显示的 “三巨头”,相比较 flex 的丰富属性,inline-block 就略显朴素,使用也十分简单,看起来似乎完全不值得对它进行详细介绍,但不知道你们在使用的时候是否也踩过那些 inline-block 的坑,所以这里我们来讲讲 inline-block 的必坑指南
我们先来看一个例子:
<div class="content">
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
</div>
.content {
background-color: yellow;
width: 350px;
height: 100px;
}
.cell {
display: inline-block;
width: 100px;
height: 50px;
background-color: red;
}
我们会发现,几个行内元素之间出现了的空隙,这个空隙实际上是因为我们的 HTML 书写导致的,我们编写代码时输入空格、换行都会产生空白符。而浏览器是不会忽略空白符的,对于多个连续的空白符浏览器会自动将其合并成一个,所以就产生了所谓的间隙。 想要解决这个间隙,我们可以给父元素设置font-size:0。
<div class="content">
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
</div>
.content {
background-color: yellow;
font-size: 0;
width: 350px;
height: 100px;
}
.cell {
display: inline-block;
width: 100px;
height: 50px;
background-color: red;
}
这个问题,是在开发的时候偶然遇见的,话不多说,先看看这两个例子:
<div class="content">
<div class="cell">有内容</div>
<div class="cell"></div>
<div class="cell">有内容</div>
</div>
.content {
background-color:
font-size: 0;
width: 350px;
height: 100px;
}
.cell {
display: inline-block;
width: 100px;
height: 50px;
font-size: 16px;
background-color:
color:
}
在这个例子里,我们发现有字的行内块元素和没有字的行内块元素显示不在同一高度。然后我们再看另一个例子:
<div class="content">
<div class="cell">有内容</div>
<div class="cell">有内容</div>
<div class="cell">有内容</div>
</div>
.content {
background-color:
font-size: 0;
width: 350px;
height: 100px;
}
.cell {
display: inline-block;
box-sizing: border-box;
width: 100px;
height: 50px;
line-height: 50px;
border-radius: 50px;
margin: 5px;
font-size: 16px;
text-align: center;
background-color:
color:
}
.active {
border: 2px solid
color:
}
let spec = document.getElementById("spec");
let flag = false;
spec.addEventListener("click", () => {
flag = !flag;
if (flag) {
spec.classList.add('active')
} else {
spec.classList.remove('active')
}
});
我们会发现,当中间的元素添加了边框后,三个行内元素都发生了上下抖动,那是否是因为他们的高度发生了变化呢?
测量结果显示,这三个元素的高度一样,并没有发生改变,那么究竟是什么原因导致了行内元素高度塌陷、上下抖动的呢?
其实造成这一结果的是vertical-align属性。inline-block作为行内块元素,也遵循行内元素的vertical-align垂直对齐方式,vertical-align的取值有以下几种:
在默认情况下,会根据baseline进行定位,文字的定位不同,最终导致了整个块元素的高度塌陷。想要解决这一问题,我们只需将vertical-align设置为top,如下: 情况一:
<div class="content">
<div class="cell">有内容</div>
<div class="cell"></div>
<div class="cell">有内容</div>
</div>
.content {
background-color:
font-size: 0;
width: 350px;
height: 100px;
}
.cell {
display: inline-block;
vertical-align: top;
width: 100px;
height: 50px;
font-size: 16px;
background-color:
color:
}
情况二:
<div class="content">
<div class="cell">有内容</div>
<div class="cell">有内容</div>
<div class="cell">有内容</div>
</div>
.content {
background-color:
font-size: 0;
width: 350px;
height: 100px;
}
.cell {
display: inline-block;
vertical-align: top;
box-sizing: border-box;
width: 100px;
height: 50px;
line-height: 50px;
border-radius: 50px;
margin: 5px;
font-size: 16px;
text-align: center;
background-color:
color:
}
.active {
border: 2px solid
color:
}
let spec = document.getElementById("spec");
let flag = false;
spec.addEventListener("click", () => {
flag = !flag;
if (flag) {
spec.classList.add('active')
} else {
spec.classList.remove('active')
}
});
run-in 这个属性值是在 css3 中被引入的,官方对他的描述是:
此元素会根据上下文作为块级元素或内联元素显示。
这个说法让人会有些摸不着头脑,所以我们直接来看个例子。
在设计文章样式的时候,也许你遇到过需要在一行内显示一个标题和一段内容的场景,如果使用h标签和p标签我们可能得到的是下面这种结果:
<div class="article">
<h1>One Point.</h1><p>Here is the description.If a run-in element precedes a block level element, the run in element will behave structurally as if it has become the block level elements first inline child element.</p>
</div>
当我们将h标签的 display 属性变成run-in时,结果就变成了这样。
h1 {
display: run-in;
}
<div class="article">
<h1>One Point.</h1><p>Here is the description.If a run-in element precedes a block level element, the run in element will behave structurally as if it has become the block level elements first inline child element.</p>
</div>
为了更好的理解 run-in,我们把它和 float,inline-block 的布局效果做比较:
h1 {
float: left;
}
<div class="article">
<h1>One Point.</h1><p>Here is the description.If a run-in element precedes a block level element, the run in element will behave structurally as if it has become the block level elements first inline child element.</p>
</div>
当使用 float 的时候,我们发现左边的标题会占据多行,这和我们的预期显然有些差距。
h1 {
display: inline-block;
}
<div class="article">
<h1>One Point.</h1><p>Here is the description.If a run-in element precedes a block level element, the run in element will behave structurally as if it has become the block level elements first inline child element.</p>
</div>
当使用 inline-block 的时候,当内容有多行,标题和内容不能在一行显示,现在我们就能够明白run-in究竟做了什么:
虽然这个属性看起来十分便利,但是这个属性已经被大多数浏览器舍弃了,这也是为什么我们平时听到的不是特别多的原因,浏览器认为 run-in 破坏了 HTML 的表现,这些样式上的实现,应该通过 CSS 去调节。 所以对于这个属性,大家做个了解就可以啦。
Flex box可能是目前用来做自适应用的最多的布局方式了,Flex 使用的丰富,也为开发者提供了更便捷更可靠的布局。
决定主轴的方向(即项目的排列方向)。取值:
决定了子元素如何换行。取值:
是 flex-direction 属性和 flex-wrap 属性的简写形式,默认值为 row nowrap。
决定了项目在主轴上的对齐方式。取值:
决定了项目在交叉轴上如何对齐。取值:
决定了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。取值:
决定项目的排列顺序。数值越小,排列越靠前,默认为 0。
决定项目的放大比例,默认为 0,即如果存在剩余空间,也不放大。
决定项目的缩小比例,默认为 1,即如果空间不足,该项目将缩小。
决定在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为 auto,即项目的本来大小。
是 flex-grow, flex-shrink 和 flex-basis 的简写,默认值为 0 1 auto。后两个属性可选。
允许单个项目有与其他项目不一样的对齐方式,可覆盖 align-items 属性。默认值为 auto,表示继承父元素的 align-items 属性,如果没有父元素,则等同于 stretch。
在 Flex box 被广泛使用以前,我们经常会利用 float 来实现块元素行内两端显示,它的使用也十分简单,float 属性有以下几种取值:
float 本身没有什么难度,但是设置了 float 会导致父元素的无法被撑开,margin 失效,所以清除 float 也就成了开发必备知识。
原始效果:
<div class="content">
<div class="cell spe1">块1</div>
<div class="cell spe2">块2</div>
</div>
.content {
background-color:
font-size: 0;
width: 350px;
}
.cell {
font-size: 16px;
background-color:
color:
}
.spe1 {
width: 80px;
height: 20px;
float: left;
}
.spe2 {
width: 80px;
height: 30px;
float: right;
}
<div class="content">
<div class="cell spe1">块1</div>
<div class="cell spe2">块2</div>
<div class="clr"></div>
</div>
.clr {
clear: both;
}
.content:after {
content: '';
display: block;
visibility: hidden;
clear: both;
}
该方法也是利用 clear:both 来清除浮动,与方法一的区别在于,方法一添加了一个 clear 元素,而这一个是在元素内部增加一个类似于 clear 的效果,其中有几个注意点:
.content {
overflow: hidden;
}
通过设置 overflow 达到清除浮动效果,其原理是创建了一个 BFC,有关 BFC 的知识,我们会在后面章节里详细介绍。
position属性决定了某个元素的定位方式,它的取值有以下几种:
在这些属性里,其中relative我们称之为相对定位,absolute为绝对定位,fixed为固定定位,sticky为粘性定位,而这几个定位又可以区分为Normal flow(常规流)和Unnormal Flow(非常规流)。
属于常规流的取值:
常规流具备以下特性:
常规流里的元素属于格式上下文(formatting context),块元素和行内元素表现不同会产生快格式上下文和行内格式上下文,也就是我们常说的BFC和IFC。
BFC 的创建方法:
BFC 的本质就是创建一个隔离的空间,他的具体效果如下:
对于上述效果,除了 1 和 3,似乎都让人有些摸不着头脑,那么就让我们通过几个例子来仔细研究一下。
首先我们来了解一下什么是外边距折叠(margin collapse)。 在 CSS 中,两个或多个毗邻的普通流中的盒子(可能是父子元素,也可能是兄弟元素)在垂直方向上的外边距会发生折叠,这种形成的外边距称之为外边距折叠。
那么什么时候外边距会发生折叠呢?发生折叠需要满足这几种情况:
下面让我们来看看代码实例:
<div class="top"></div>
<div class="content">
<div class="cell"></div>
<div class="cell"></div>
</div>
<div class="foot">
<div class="cell2"></div>
<div class="spe"></div>
<div class="cell2"></div>
</div>
.top {
background-color:
width: 100px;
height: 50px;
}
.content {
margin-top: 10px;
background-color:
width: 350px;
margin-bottom: 10px;
}
.cell {
margin-top: 20px;
background-color:
height: 20px;
margin-bottom: 30px;
}
.foot {
background-color:
width: 100px;
}
.spe {
margin: 20px 0 30px;
}
.cell2 {
height: 20px;
background-color:
}
现在我们知道了 margin collapse 发生的情况,那我们盖如何避免边距折叠呢?方法有以下几种:
<div class="BFC">
<div class="left"></div>
<div class="right"></div>
</div>
.BFC {
background-color:
}
.left {
float: left;
background-color:
width: 100px;
height:50px;
opacity: 0.6;
}
.right {
width: 150px;
background-color:
height:30px;
}
在我们没有创建 BFC 的时候,父元素的高度不包含浮动元素高度。
.BFC {
background-color:
overflow: hidden;
}
创建 BFC 后,父元素就会加上浮动元素高度,这个就是我们在之前提到的清除浮动的方法之一。
<div class="BFC content">
<div class="left"></div>
<div class="right BFC">
<div class="cell"></div>
<div class="cell"></div>
</div>
</div>
.BFC {
overflow: hidden;
}
.content {
background-color:
}
.left {
float: left;
background-color:
width: 100px;
height:50px;
opacity: 0.6;
}
.right {
width: 150px;
background-color:
height:30px;
}
.cell {
width: 20px;
margin: 5px;
background-color:
height:20px;
display: inline-block;
}
从这个例子,我们就可以理解,BFC 布局规则里的第 4 和第 6 条:
IFC 由 inline 元素构成,它的布局规则是这样的:
那么 IFC 一般有什么用呢?
属于非常规流的取值:
对于非常规流,因为他们脱离了正常的文档流,所以他们不会对正常流的元素造成影响,很多时候我们都会配合z-index使用,前两种相信大家都不陌生,所以就不再多做介绍啦,下面我们来讲讲sticky。
粘性定位(sticky) 可以被认为是相对定位和固定定位的混合。元素在跨越特定阈值前为相对定位,之后为固定定位。
<div class="content">
<h1 class="title">我是一块内容</h1>
<div class="sticky"></div>
<div class="main"></div>
</div>
.content {
background-color:
}
.title {
color:
}
.sticky {
position: sticky;
top: 0px;
background-color:
width: 100px;
height:50px;
}
.main {
width:150px;
height: 900px;
}
使用 sticky 布局的时候有几个注意点:
VFC,也就是Visual formatting context,虚拟格式上下文,他有BFC,IFC,GFC和FFC这几种类型,在前文中,我们已经介绍了BFC,IFC,这里再简单介绍下GFC和FFC。
GFC(GridLayout Formatting Contexts),"网格布局格式化上下文",当为一个元素设置 display 值为 grid 的时候,此元素将会获得一个独立的渲染区域。
我们可以通过在网格容器(grid container)上定义网格定义行(grid definition rows)和网格定义列(grid definition columns)属性各在网格项目(grid item)上定义网格行(grid row)和网格列(grid columns)为每一个网格项目(grid item)定义位置和空间。
那么 GFC 有什么用呢,和 table 又有什么区别呢?首先同样是一个二维的表格,但 GridLayout 会有更加丰富的属性来控制行列,控制对齐以及更为精细的渲染语义和控制。
FFC(Flex Formatting Contexts),自适应格式化上下文,display 值为 flex 或者 inline-flex 的元素将会生成自适应容器(flex container)。 所以当我们使用 flex 布局的时候,我们就创建了一个 FFC。

