20

前言

本文将带你重新认识CSS布局,带你解剖布局原理,前提是你要有基础!本文将解除你在布局方面的疑惑。认识每个布局元素,了解他们的特性,你才知道为什么会是这样的结果。本文内容纯属个人理解,不代表官方。

此文主要为理论部分,实际例子可以看我另外一篇文章 PC端CSS布局汇总

学习CSS布局前你需要了解这些

首先了解下盒子模型

clipboard.png

上图是一个盒子模型,每个html标签都会转化成一个盒子模型,每个盒子都有自己的positionmarginborderpaddingwidthheight,这些属性决定着盒子的大小和位置。盒子的大小由borderpaddingwidthheight共同决定。盒子的位置是我们接下来要探讨的问题。

浏览器审查元素都能看到每一个标签的盒子模型。看盒子模型有些要注意的

  • 图中的数值都是px单位的,其他单位都会换算成px。填横线-的表示默认值,有可能是0,也有可能是auto
  • 图中的蓝色区域820 x 26表示宽度 x 高度,是最终计算出来的实际宽高,而不是css里设置的width和height。如果设置了box-sizing:border-box,系统会自动减去paddingborder的大小,计算后的显示在蓝色区域,这个区域是实际可用空间。
  • 图中的position表示topbottomleftright的值,定位元素才有这项。

html的标签结构是属于树形结构,转化成盒子模型就变成一个套一个,最外层是document,再往里一层是<html><html>里面放置两个盒子headbody,以此类推。CSS布局就是在探究盒子在父容器(上一级盒子)里的放置位置。

盒子的放置位置与盒子大小、盒子之间的间距有关,也就是盒子模型上的那几个属性有关。每种标签对盒子模型的处理方式有些差异,我把这些盒子归为这几类:内联元素、块级元素、内联块元素、表格元素、弹性盒子元素、浮动元素、定位元素。这些元素我统称他们为布局元素。

盒子的放置位置还与盒子的排列方式有关,是从左到右排呢?还是从右到左排呢?是否允许重叠?盒子的排列方式就是所谓的文档流,文档流一般分为三种:常规文档流、BFC、脱离文档流。

以下将对布局元素和文档流进行详细讲解。

布局元素

CSS把这些布局元素分为三大类,分别用displayfloatposition来声明。其中display用来声明:内联元素、块级元素、内联块元素、表格元素、弹性盒子元素。float用来声明浮动元素。position用来声明定位元素。这三大类可以混合使用,其中display为必须项,你不设置它也有默认值。

1. 内联(行内)元素

display: inline;

宽高大小由子元素决定,不能手动修改宽高,子元素一般放置文本元素,与其他内联元素并排在同一行

clipboard.png

内联元素不能改变宽高,导致有些属性无效,比如:width系列、height系列、margin-topmargin-bottompadding-toppadding-bottomline-height

常用的内联标签:<span><img><em><strong><i>

使用内联元素你可能会遇到这种情况,

<div>
    <span style="background: #ccf">内联元素</span>
    <span style="background: #fcc">内联元素</span>
</div>

clipboard.png

内联元素之间有空白区域

空白区域的形成是因为<span>之间有回车,在html中,空格、制表符、回车都属于空白符,多个空白符都会视为一个空格,空格的大小由父级<div>font-size决定。
注意:只有内联(内联块)与内联(内联块)之间的空白符才会形成一个空格,文本元素(除空白符)也是属于内联元素。

解决空白区域的方案有以下四种

  • <div>设置font-size: 0;,在<span>上把font-size设置回去
  • 将空白符注释掉
  • 把span排列在同一行,并紧贴在一起
  • 使用浮动float,有些场合不允许使用浮动,这条就不适用

2. 块级(区块)元素

display: block;

默认高度等于子元素高度,宽充满父级元素,块级元素之间纵向排列

clipboard.png

常用的块级标签:<div><h1> to <h6><p><ul> ~ <li><dl> ~ <dt> <dd>

块级元素之间如果不浮动或定位,永远是纵向排列,不管宽度多少。

块级元素宽高默认为auto,有自适应伸缩的特性。例如:

<div style="float: left; background: #ccf">浮动元素</div>
<div style="height: 40px; background: #fcc">块级元素</div>

clipboard.png

块级元素的文档流不被破坏的情况下,宽度为auto时会永远充满容器宽度,遇到有东西挡住,文本流会自动往后移,但实际区域没变,只是被遮住了。可以借助这个特性做图文排版,或者做自适应宽度布局。注意:块级元素一旦脱离的文档流,这个特性将会失效

块级元素还有另一大特性,文档流不被破坏的情况下,外边距margin会自动填充横向剩余部分

<div style="width: 80px; height: 40px; background: #fcc">块级元素</p>

clipboard.png

橙色部分表示marginmargin默认是0,却能充满剩余部分,这就解释了为什么块级元素永远是纵向排列。如果margin的值设置为auto,它就会左右平分掉,形成了区块横向居中的现象。

块级元素处理盒子模型较为灵活,通常优先使用块级元素布局,块级元素无法实现的情况才采用其他元素布局,它擅长于处理自身与父元素和兄弟元素之间的布局,不擅长对子元素布局

3. 内联块元素


display: inline-block;

与内联元素一样,默认宽高由子元素决定,但它可以设置宽高、边距。内联块之间横向排列

clipboard.png

内联块元素是内联元素与块级元素的结合体,拥有内联元素的特和块级元素的灵活性,但它没有块级元素的特性。它也会有元素间出现空白区域的问题,解决方案一样。

内联块元素有自己的一大特性,就是它可以将自身(包括子元素)当成一个文本元素去操作,像操作文本一样去操作一块区域,如下例子:

<div class="parent">
    <div class="box"></div>
    <div class="box"></div>
</div>
.parent {
    width: 400px;
    height: 80px;
    font-size: 0;            /* 防止空白区域 */
    text-align: center;
    text-indent: -40px;      /* 文本缩进 */
    letter-spacing: 30px;    /* 文字之间的间距 */
    background: #ccc;
}
.box {
    display: inline-block;
    width: 100px;
    height: 80px;
    background: #fcc;
    font-size: 16px;
}

clipboard.png

给文本设置居中,设置文本向左缩进40px,文字间距30px。这些都是对文字的设置,但内联块却生效了,两个内联块被看成两个单词,所以间距才会生效。

有了这个特性,就能完成一些很细微的布局操作,在实际开发中也是挺常用的,通常被当成一个容器结合其他元素一起使用。

4. 表格元素

display: table;      /*对应<table>*/
display: table-cell; /*对应<td>*/

与标签<table>系列一样,<tr>的宽度会充满<table>,而<td>宽度会平分<tr><td>内容超出宽度默认会撑开。当然你也可以设置让它不撑开。

使用CSS定义可以将普通的标签变成表格元素,tr不能设置width,所以一般不使用tr,也就是CSS中的table-row,所以通常使用tabletd,不需要trtd也是可以平分table宽度的,结构关系一定要是父子关系。

<div class='parent'>
    <div class="child child1">1111</div>
    <div class="child child2">2222</div>
    <div class="child child3">3333</div>
</div>
.parent {
    display: table;
    width: 200px;
    height: 40px;
    background: #ccc;
}
.child {
    display: table-cell;
}
.child1 { background: #0cc; }
.child2 { background: #c0c; }
.child3 { background: #cc0; }

clipboard.png

子元素并没有设置宽高,却能填充高度,平分宽度,这就是表格元素的一大特性。用过table布局的人应该都清楚,如果td内容太多,宽度会自动撑开,占用周围的td宽度,table-cell也一样,不过不想让它自动撑开,就在table层设置

table-layout: fixed;

表格元素比较擅长于做居中布局,因为table-cell元素支持vertical-aligntext-align这两个属性,对子元素进行横向纵向居中,把子类设置为inline-block就可以区域居中,而且不需要知道子类宽高。

表格元素也可以让自身横向居中,设置方式跟块级元素一样,设置margin: 0 auto,不同的是表格元素不需要知道宽度。

5. 弹性盒子元素

弹性盒子是CSS3引入布局方式,它将更加有效的对一个容器中的元素进行排列、对齐和分配空白空间。本文章的讲解不涉及弹性盒子元素,因为其他布局能做的,弹性盒子元素绝大部分都能做,只是兼容性较差,不适用于PC端,有人已经做了详细讲解,推荐看这篇文章 阮一峰Flex布局教程

6. 浮动元素

float: none;   /* 取消浮动 */
float: left;   /* 左浮动 */
float: right;  /* 右浮动 */

float不为none的属于浮动元素

浮动元素强制让元素向左或向右贴近,如果同一个方向有多个元素,则会横向排列,并紧贴在一起,若空间不够,则会换行,如下图所示。

clipboard.pngclipboard.pngclipboard.png

浮动元素会让元素脱离文档流,其他元素将无视浮动元素,把浮动元素的位置给占了,如下例子

<div class="parent">
    <div class="child child1">浮动元素</div>    
    <div class="child child2">浮动元素</div>
</div>
<div class="box">块级元素</div>
.child{
    float: left;
    width: 100px;
    height: 40px;
}
.child1 {background: #fcc;}
.child2 {background: #ccf;}

.box{
    height: 60px;
    background: #ccc;
}

clipboard.png

脱离文档流的元素的层级会比较高,会叠在上面。

浮动元素脱离文档脱离的不够彻底,属于部分脱离,可以将它拉回文档流,让.box位于.child下面。有两种方式:

第一种是使用清除浮动clear,有的人可能会在.box上写clear: both来清除浮动,这样是可以达到效果,但会引起一个问题,margin-top无效。应该在.box之前插入一个空元素,使用一个空元素专门用来清除浮动,这个空元素可以使用CSS伪元素代替。所以清除浮动的代码应该是这样

.parent:after {
    content: '';
    display: table;
    clear: both;
}

第二种方式是将浮动元素的父元素转化为BFC,BFC是什么后面会讲解,现在先看看实现代码

.parent {
    overflow: hidden;
}

一般使用最多的是第一种,因为在空元素设置不会受到任何影响。在PC端清除浮动较为常用,一般会把清除浮动的代码放在.clearfix上,页面上需要清除浮动再使用这个class,或者sass继承。

浮动元素在PC端非常受欢迎,它可以将块级元素横向排列。书写简单,只要一个float: left就好

浮动元素最大的特点是它可以让一个元素单独居左或居右,而不影响其他元素。而且还能保持文档流,这是其他元素做不到的。

7. 定位元素

position: static;    /* 取消定位 */
position: relative;  /* 相对定位 */
position: absolute;  /* 绝对定位 */
position: fixed;     /* 固定定位 */

position不为static的属于定位元素。定位元素分为三种:相对定位、绝对定位、固定定位。三种都有topbottomleftrightz-index属性,都是基于某个参照物进行定位,不同的是定位参照物不同和文档流不同,以下是各自的特点和用法。

相对定位属于常规文档流的,与块级元素一样的排列,它的定位参照物是自身,设置left就会向右移,设置bottom会向上移,righttop同理,它不像margin,它位移只对自身有影响,不会影响其他元素,所以可能会导致覆盖其他元素的现象。相对定位可以用来设置定位参照物,方便绝对定位操作。

绝对定位是脱离文档流的,而且脱离得很彻底,跟浮动元素不一样,它无法拉回文档流,它也属于BFC。它的定位参照物不固定,如果父元素是个定位元素,就优先使用父元素作为定位参照物,不是定位元素就往上一级找,直到检测到定位元素,如果到达<html>还是没有,就以浏览器第一视口作为定位参照物。

clipboard.png

浏览器视口指蓝色区域区域,浏览器第一视口指滚动条置顶状态下的视口。

固定定位跟绝对定位一样,唯一的差别是固定定位的定位参照物固定是浏览器视口。

绝对定位和固定定位有个特性,设置leftright,会锁定这两点之间的空间。如果没设置widthwidth: auto,就会填充这个空间。如果设置了widthmarginauto就会生效,会在锁定空间内居中。这个特性在纵向的heighttopbottom会有同样的效果。

文档流

常规文档流

从左到右一个一个盒子以指定间距排列,排不下就跳到下一行继续排列。此文档流的特性将在BFC进行对比详解。

BFC

BFC全称块级格式化上下文(Block Formatting Context),属于常规文档流的改进版,在此文档流下的盒子将是一个独立的盒子,什么意思?难道常规文档流还能共享盒子不成?还真是了。其实我们用的最多的是BFC,如果你不了解BFC,实际开发中可能会一不小心用了常规文档流,出现了某些的现象会觉得是bug,其实那是文档流的特性。接下来列举几个常规文档流现象:

1. 边距折叠

<div class="box1">box1</div>
<div class="box2">box2</div>
.box1 {
    height: 60px;
    background: #fcc;
    margin-bottom: 10px;
}
.box2 {
    height: 60px;
    background: #ccf;
    margin-top: 20px;
}

clipboard.png

大多时候,我们要的是30px的间距,但实际却是20px,那是因为边距被折叠了,只会取最大的边距。这种的情况解决方案是把他们放在不同的BFC内,这个这样改

<div class="wrap">
    <div class="box1">box1</div>
</div>
<div class="box2">box2</div>
.wrap {
    overflow: hidden;
}
.box1 {
    height: 60px;
    background: #fcc;
    margin-bottom: 10px;
}
.box2 {
    height: 60px;
    background: #ccf;
    margin-top: 20px;
}

BFC是独立的盒子,自身的margin不会与其他盒子融合,所以外边距只会叠加,不会折叠。

2. 边距侧漏

常规文档流里的块级元素会有这样的问题

<div class="parent">
    <div class="box">box</div>
</div>
.parent {
    height: 60px;
    background: #ccf;
}
.box {
    margin-top: 20px;
}

这段代码应该是把文字把往下移20像素,结果应该是这样的
clipboard.png

然而并不是,其实结果是这样
clipboard.png

块级元素的第一个子元素的margin-top会穿过父元素的屏障,漏出去了,变成是父元素的margin-top。你可能会想到给父元素加一层屏障,设置border-top,但这样会无故多出一个边框的空间。最好的方式是把父元素转化为BFC,这样这样改

.parent {
    height: 60px;
    background: #ccf;
    overflow: hidden;
}

BFC内部元素怎么折腾怎么改都不会影响父容器。

BFC除了纠正常规文档流的问题外,还有一些对浮动元素的纠正。

3. 清除浮动

<div class="parent">
    <div class="child child1">浮动元素</div>    
    <div class="child child2">浮动元素</div>
</div>
<div class="box">块级元素</div>
.child{
    float: left;
    width: 100px;
    height: 40px;
}
.child1 {background: #fcc;}
.child2 {background: #ccf;}

.box{
    height: 60px;
    background: #ccc;
}

还是拿之前浮动元素的例子来讲,当时没讲为什么BFC可以清除浮动,现在来讲下原理。细心的同学应该可以发现,.child有设置宽高,可身为父元素的.parent却没有高度,所以才导致.box无视浮动元素占了它的位置。如果.parent能得到子元素的高度就符合了常规文档流,就达到清除浮动浮动的效果。给.parent设置为BFC就能做到

.parent {
    overflow: hidden;
}

因为BFC的子元素怎么变化都不会影响父容器,子元素做了浮动,那是子元素的事,你所占有的宽高还是会把父容器撑开。

4. 防止文字环绕

有些场合并不想让它文字环绕,如下代码

<div class="float"></div>
<div class="text">这是一段文本这是一段文本这是一段文本这是一段文本这是一段文本这是一段文本这是一段文本这是一段文本这是一段文本这是一段文本这是一段文本这是一段文本</div>
.float {
    width: 50px;
    height: 50px;
    float: left;
    background: #fcc;
}
.text {
    height: 100px;
    background: #ccf;
}

clipboard.png

.text设置为BFC可以防止文字环绕

.text {
    height: 100px;
    background: #ccf;
    overfloat: hidden;
}

clipboard.png

以上是BFC常见的运用场合,可能有些人没遇到这些场合,因为你们正无形中在使用BFC,上面的例子都是用overflow:hidden设置BFC,其实设置方式有很多,满足以下条件的任何一条都是BFC

  • float不为none
  • position 的值不为 static 或者 relative
  • display 的值为 table-cell , table-caption , inline-block , flex , 或者 inline-flex 中的其中一个
  • overflow 的值不为 visible

除了BFC外其他都是常规文档流。

脱离文档流

脱离文档流是基于BFC进行改造去除一些常规文档流的东西。脱离文档流又分为两种:部分脱离和完全脱离。

部分脱离是对常规文档流的排列顺序进行改造,不再只是从左到右排列,还可以从右到左,也可以一左一右,为了不影响常规文档流的排列,默认会脱离文档流,但还是会受父容器束缚。属于部分分离的是float:leftfloat:right

完全脱离是完全放弃常规文档流,不受任何束缚,根据leftrighttopbottom去定位。属于完全脱离的是position:absoluteposition:fixed


Jencia
136 声望7 粉丝