虽然也知道float布局、 表格布局,但实在是用的不太多。加上现在比较新的flex布局和Grid布局,还是需要做一个系统的总结。
由于HTML表格布局是旧Web时代的遗留产物,不能语义化文档内容,对屏幕阅读器的适配和可读性都不太友好,所以是应该舍弃的布局方式。
那么按照MDN上的文档,布局分为以下5种:

  • 浮动布局 Floats
  • 定位布局 Positioning
  • CSS表格布局
  • 弹性盒子布局 Flexbox
  • 网格布局 Grid

浮动布局


float允许元素脱离脱离正常的文档布局流,并粘贴到其父容器的一侧。在正常布局中位于该浮动元素之下的内容,此时会围绕着浮动元素,填满其右侧的空间。主要用于对列式内容进行布局或者使文字浮动于图像周边。
float有4种值

  • left — 将元素浮动到左侧
  • right — 将元素浮动到右侧
  • none — 默认值, 不浮动
  • inherit — 继承父元素的浮动属性

文字包围图像

1
2
3
4
5
<h1>Simple float example</h1>

<img src="butterfly.jpg" alt="A pretty butterfly with red, white, and brown coloring, sitting on a large leaf">

<p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla luctus aliquam dolor, eu lacinia lorem placerat vulputate. Duis felis orci, pulvinar id metus ut, rutrum luctus orci. Cras porttitor imperdiet nunc, at ultricies tellus laoreet sit amet. Sed auctor cursus massa at porta. Integer ligula ipsum, tristique sit amet orci vel, viverra egestas ligula. Curabitur vehicula tellus neque, ac ornare ex malesuada et. In vitae convallis lacus. Aliquam erat volutpat. Suspendisse ac imperdiet turpis. Aenean finibus sollicitudin eros pharetra congue. Duis ornare egestas augue ut luctus. Proin blandit quam nec lacus varius commodo et a urna. Ut id ornare felis, eget fermentum sapien.</p>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
body {
width: 90%;
max-width: 900px;
margin: 0 auto;
}

p {
line-height: 2;
word-spacing: 0.1rem;
}
img {
float: left;
margin: 0 30px 0 0;
}

实现这样的浮动效果
image_1bo05uvbicif16c113tj1mp4rrd9.png-254.6kB

多列浮动布局

通常使用宽度加浮动属性以及合适的外间距来布局列式内容(侧边栏等)。

1
2
3
4
div {
width: 48%;
float: right;
}

但是这样的布局容易出现float元素后边的元素”挤”到几个float元素中间的情况。特别是多列内容长度相差较大的时候,看起来比较”terrible”。
image_1bo06j2mi4m9dmi1vr61tql49nm.png-103.4kB
这时候就需要在下部元素添加clear属性。

浮动布局问题

以上部分提供了使用浮动创建简单布局的基础,但是还有一些问题需要解决。

  • 整个宽度可能难以计算
    一旦加上内边距或者影响宽度的属性,多列布局宽度可能超过父元素,出现降列的情况。
    解决方法是将盒模型中宽度变为content + padding + border,而不是原本的content宽度:

    1
    2
    3
    * {
    box-sizing: border-box;
    }
  • 使用clear属性后无法正常使用浮动元素与非浮动元素的外边距
    在浮动元素和非浮动元素中加入空元素后,下方footer可使用外边距

    1
    <div class="clearfix"></div>
  • 在有背景存在的情况下多列内容背景高度不一致
    尽管我们可以通过设置高度来时解决这个问题,但显然不是一个优雅的办法,毕竟不能强行控制栏内内容的多少。即便通过overflow来控制溢出显示。

    1
    2
    3
    .column {
    height: 550px;
    }

    另外一种解决办法是使用伪列,通过将背景图像沿y轴拓展,看起来像是背景高度一致了。但该方法不能使用列边框。
    所以这是flexbox布局出现的原因,flex布局能够自动拓展列的高度到多列中的最高。

  • 还有其他怪异问题和对应解决办法

定位布局


正常文档是按照块级元素从上至下,内联元素在一行内进行布局的;块级元素的内容宽度是其父元素的宽度的100%,并且与其内容一样高。内联元素高度与宽度均与内容一致,不能设置宽高。
相邻元素存在外边距折叠的现象。

position属性

  • 静态定位(static)是每个元素默认的属性——它表示“将元素放在文档布局流的默认位置——没有什么特殊的地方”.
  • 相对定位(relative)允许我们相对元素在正常的文档流中的位置移动它——包括将两个元素叠放在页面上. 相对定位在对布局进行微调的时候非常有用.
  • 绝对定位(absolute)将元素完全从页面的正常布局流中移出, 类似将它单独放在一个图层中. 我们可以将元素相对页面的 元素边缘固定(或者它最近的被定位的上层元素). 绝对定位在创建复杂布局效果时非常有用,例如通过标签显示和隐藏的内容面板或者通过按钮控制滑动到屏幕中的信息面板.
  • 固定定位(fixed)与绝对定位非常类似, 除了它是将一个元素相对浏览器视口固定,而不是相对另外一个元素. 在创建类似页面滚动总是处于页面上方的导航菜单时非常有用.
  • 黏性定位(sticky) 该属性值尚未广泛使用。这基本上是相对定位和固定定位之间的混合,其允许定位的元素一开始表现为相对定位,直到其滚动到某一阈值点(例如,从视口顶部10像素),之后它变得固定。
  • top, bottom, left, 和 right 来精确指定要将定位元素移动到的位置。

相对定位

相对定位的元素使用top, bottom, left, 和 right定位后,相对其原本位置进行移动;不脱离文档布局流,原始占位不变。

绝对定位

元素的绝对定位是相对其包含块(containing block)的,一旦设置为绝对定位,会向父级一直寻找非静态定位元素(relative,absolute,fixed),若没有则是html元素,相对最近的包含块进行定位。

另外多个元素定位发生重叠后,原本在z轴的表现顺序是按照文档顺序体现的;使用 z-index属性可以设置在z轴上的层叠关系。

表格布局

通过设置

1
2
3
4
display: table;
display: cell-row;
display: cell-column;
display: tabel-cell;

来进行表格布局。

Flex布局


浮动布局与定位布局由于实现以下布局较为复杂:

  • 垂直于父元素中心
  • 容器的所有元素占据高度/宽度的一定比例,无论父元素的宽高
  • 所有列无论内容多少,高度一致

flex布局使得很多布局任务变得更加容易。首先设置container元素显示:

1
2
3
section {
display: flex;
}

则section内的元素会呈现多列布局,且高度一致。

flex模型说明

当元素表现为flex框时,它们沿着两个轴来布局:
image_1bo1b91qj1pq61ndg18a01jt90i9.png-32.1kB

  • 主轴(main axis)是沿着 flex元素放置的方向延伸的轴(比如页面上的横向的行、纵向的列)。该轴的开始和结束被称为 main start 和 main end。
  • 横轴(cross axis)是垂直于flex元素放置方向的轴。该轴的开始和结束被称为cross start和cross end。
  • 设置了display: flex的父元素被称之为flex容器(flex container)。
  • 在flex容器中表现为flexible框的元素被称之为flex项(flex item)。

flex-direction

该属性指定主轴的方向,默认值为row,还可以设置为col row-reverse column-reverse。

flex-wrap

当flex-item为定宽/高时,一旦元素数量过多,可能会溢出flex container。此时需要设置自动换行flex-wrap: wrap

上述两个属性可以缩写为flex-flow: row wrap

flex item的flex属性

flex是一个可以指定最多三个不同值的缩写属性:

  • 第一个是无单位比例。可以单独指定全写flex-grow属性的值。
  • 第二个无单位比例(flex-shrink)一般用于溢出容器的flex项。这指定了从每个flex项中取出多少溢出量,以阻止它们溢出它们的容器。 这是一个相当高级的flexbox功能。
  • 第三个是最小值。可以单独指定全写flex-basis属性的值。

flex:1表示每个flex项沿主轴的可用空间大小。每个元素占用空间都是相等的,占用的空间是在设置 padding 和 margin 之后剩余的空间。因为它是一个比例,这意味着将每个 flex 项的设置为 400000 的效果和 1 的时候是完全一样的。

flex:2则该元素的占据2倍的宽度。
flex: 1 200px则首先分配200px的最小空间,剩下空间按照比例共享。

水平和垂直对齐

1
2
3
4
5
div {
display: flex;
align-items: center;
justify-content: space-around;
}

align-items控制flex项在横轴上的位置。

  • 默认值是stretch,其会使所有flex项沿着横轴的方向拉伸以填充父容器。如果父容器在横轴方向上没有固定宽度(即高度),则所有flex项将变得与最长的flex项一样长(即高度保持一致)。
  • center 值会使这些项保持其原有的高度,但是会在横轴居中(主轴为row时表现为垂直居中)。
  • flex-start或flex-end这样使flex项在横轴的开始或结束处对齐所有的值。

可以用单个flex item的align-self属性覆盖align-items的行为。比如

1
2
3
button:first-child {
align-self: flex-end;
}

justify-content 控制 flex 项在主轴上的位置。

  • 默认值是flex-start,这会使所有flex item沿着主轴的起点紧贴布局。
  • flex-end 则与flex-start相反,从end处紧贴向开始布局。
  • center,可以让flex项在主轴居中紧贴布局。
  • space-around 会使所有flex项沿着主轴均匀地分布,在任意一端都会留有一点空间。
  • space-between,它和space-around非常相似,只是它不会在两端留下任何空间。

flex 项排序

flex布局可以改变flex item的布局位置的功能,而不会影响到源顺序(即 dom 树里元素的顺序)。这也是传统布局方式很难做到的一点。

1
2
3
button:first-child {
order: 1;
}

刷新下,然后你会看到 “Smile” 按钮移动到了主轴的末尾。

  • 所有flex项默认的order值是 0。
  • order值大的flex项比order值小的在显示顺序中更靠后。
  • 相同order值的flex项按源顺序显示。所以假如你有四个元素,其 order 值分别是2,1,1和0,那么它们的显示顺序就分别是第四,第二,第三,和第一。
  • order设置负值使它们比值为0的元素排得更前面。比如,你可以设置 “Blush” 按钮排在主轴的最前面:
    1
    2
    3
    button:last-child {
    order: -1;
    }

flex 嵌套

设置flex项为flex容器可以支持嵌套。可以实现以下布局:
image_1bo1e8bnj1tcm1srg1h0u1eutn7am.png-107.4kB

属性总结

flex-direction
flex-wrap
flex-flow
justify-content
align-items
align-content
Flex items properties

order
align-self
flex-grow
flex-shrink
flex-basis

Grid布局

许多现代网站布局是基于规则网格。
网格仅仅是水平和垂直线的集合,创建了一种模式,我们可以根据这种模式排列我们的设计元素。他们帮助我们创建设计,其中元素不跳动或改变宽度,因为我们从一个页面到另一个页面,提供更大的一致性在我们的网站。

网格通常具有列,行,然后在每行和列之间的间隙通常称为沟槽(gutters)。
image_1bo1f6j586rb10rvggtul3lhq2j.png-32.8kB
对于如下页面,创建12列固定宽度的网格布局,每列60px,间沟槽20px,共6012+1320=980px;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div class="wrapper">
<div class="row">
<div class="col">1</div>
<div class="col">2</div>
<div class="col">3</div>
<div class="col">4</div>
<div class="col">5</div>
<div class="col">6</div>
<div class="col">7</div>
<div class="col">8</div>
<div class="col">9</div>
<div class="col">10</div>
<div class="col">11</div>
<div class="col">12</div>
</div>
<div class="row">
<div class="col span1">13</div>
<div class="col span6">14</div>
<div class="col span3">15</div>
<div class="col span2">16</div>
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
* {
box-sizing: border-box;
}

body {
width: 980px;
margin: 0 auto;
}

.wrapper {
padding-right: 20px;
}
.row {
clear: both;
}

.col {
float: left;
margin-left: 20px;
width: 60px;
background: rgb(255, 150, 150);
}

/* Two column widths (120px) plus one gutter width (20px) */
.col.span2 { width: 140px; }
/* Three column widths (180px) plus two gutter widths (40px) */
.col.span3 { width: 220px; }
/* And so on... */
.col.span4 { width: 300px; }
.col.span5 { width: 380px; }
.col.span6 { width: 460px; }
.col.span7 { width: 540px; }
.col.span8 { width: 620px; }
.col.span9 { width: 700px; }
.col.span10 { width: 780px; }
.col.span11 { width: 860px; }
.col.span12 { width: 940px; }

创建流体网格

流体网格随着浏览器视口中的可用空间而增长和缩小。将像素宽度转化为百分比可以实现:
60 / 960 = 0.0625;20 / 960 = 0.02083333333;
更改CSS规则:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
.wrapper {
padding-right: 2.08333333%;
}
.col {
float: left;
margin-left: 2.08333333%;
width: 6.25%;
background: rgb(255, 150, 150);
}
/* Two column widths (12.5%) plus one gutter width (2.08333333%) */
.col.span2 { width: 14.58333333%; }
/* Three column widths (18.75%) plus two gutter widths (4.1666666) */
.col.span3 { width: 22.91666666%; }
/* And so on... */
.col.span4 { width: 31.24999999%; }
.col.span5 { width: 39.58333332%; }
.col.span6 { width: 47.91666665%; }
.col.span7 { width: 56.24999998%; }
.col.span8 { width: 64.58333331%; }
.col.span9 { width: 72.91666664%; }
.col.span10 { width: 81.24999997%; }
.col.span11 { width: 89.5833333%; }
.col.span12 { width: 97.91666663%; }

使用calc() 函数更容易的计算

1
2
3
4
5
6
7
8
9
10
11
.col.span2 { width: calc((6.25%*2) + 2.08333333%); }
.col.span3 { width: calc((6.25%*3) + (2.08333333%*2)); }
.col.span4 { width: calc((6.25%*4) + (2.08333333%*3)); }
.col.span5 { width: calc((6.25%*5) + (2.08333333%*4)); }
.col.span6 { width: calc((6.25%*6) + (2.08333333%*5)); }
.col.span7 { width: calc((6.25%*7) + (2.08333333%*6)); }
.col.span8 { width: calc((6.25%*8) + (2.08333333%*7)); }
.col.span9 { width: calc((6.25%*9) + (2.08333333%*8)); }
.col.span10 { width: calc((6.25%*10) + (2.08333333%*9)); }
.col.span11 { width: calc((6.25%*11) + (2.08333333%*10)); }
.col.span12 { width: calc((6.25%*12) + (2.08333333%*11)); }

启用偏移容器

1
2
3
.offset-by-one {
margin-left: calc(6.25% + (2.08333333%*2));
}

image_1bo1hupqoppv1tg218tg1vtrtug30.png-4.7kB

flexbox实现网格布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.row {
display: flex;
}

.col {
margin-left: 2.08333333%;
margin-bottom: 1em;
width: 6.25%;
flex: 1 1 auto;
background: rgb(255,150,150);
}
.col.span2 { width: calc((6.25%*2) + 2.08333333%); }
.col.span3 { width: calc((6.25%*3) + (2.08333333%*2)); }
.col.span4 { width: calc((6.25%*4) + (2.08333333%*3)); }
.col.span5 { width: calc((6.25%*5) + (2.08333333%*4)); }
.col.span6 { width: calc((6.25%*6) + (2.08333333%*5)); }
.col.span7 { width: calc((6.25%*7) + (2.08333333%*6)); }
.col.span8 { width: calc((6.25%*8) + (2.08333333%*7)); }
.col.span9 { width: calc((6.25%*9) + (2.08333333%*8)); }
.col.span10 { width: calc((6.25%*10) + (2.08333333%*9)); }
.col.span11 { width: calc((6.25%*11) + (2.08333333%*10)); }
.col.span12 { width: calc((6.25%*12) + (2.08333333%*11)); }

但是flex布局本质上还是一维布局,需要计算宽度百分比;

第三方grid布局

比如BootstrapFoundation