阅读 91

CSS布局指导

原文链接: mp.weixin.qq.com

过去几年, CSS的布局方式如同Web前端的开发方式一样, 变化巨大. 现在的CSS已经提供了多种可选布局方式. 我们需要从中选择一个最恰当的布局方式. 这篇文章介绍了不同布局的原理和用法, 已经我们该如何使用它们.

这篇文章主要针对CSS布局的新手, 如果你已经有很多布局上的经验, 也可以用这篇文章来检验一下对于布局的理解是否深入或者看看其他人是如何使用CSS布局的. 当然本文不会完整的描述各个布局的细节, 仅仅是一个布局方式的预览, 想要了解更多可以参考文中的更多内容的链接.


标准流布局 (Normal Flow)

如果不在网页中添加任何CSS, 那么这个"原生态"网页采用的就是标准流布局, 在标准流布局中, 所有的盒子模型会根据文档的阅读方向, 一个接一个的拼凑在一起. 如果你的浏览器文字方向是水平的, 那么所有元素由左向右的排列, 当一行内容显示不下时, 下一个元素会另起一行继续显示. 如果你的浏览器文字方向是垂直的, 那么元素的排列就是从上到下.

 

标准流布局会根据文字方向的不同而不同

标准流布局是所有CSS布局的默认方式, 当我们采用其它布局时, 意味着我们创建了一个区块, 并使得这个区块使用了非标准留的布局方式.

合理规划文档布局, 充分发挥标准流优势

一个经过精心规划的文档结构, 可以发挥标准流布局的巨大优势. 试想一下, 如果没有标准流布局的默认行为, 浏览器会在自定义布局的样式完全加载前, 把所有元素都挤在页面最开始的一个小框框里面. 这就意味着, 我们不仅需要一个自定义布局, 在这个自定义的布局中还需要针对每一个元素做出准确的布局定义. 浏览器当时没有这么傻, 所以浏览器会在没有明确布局定义的时候采用标准流作为默认的布局方式. 将所有元素以一种便于阅读的方式呈现.

是时候展现复杂的布局了

当我们完成了文档的结构化之后, 我们就需要思考什么才是我们最期望的布局. 这就需要我们把文档的一部分从标准流布局中摘出来. 能够实现非标准流布局的方式有很多, 首先我面来了解一下 float 布局. 因为浮动布局是一个典型的将元素移出标准流布局的方式.

浮动布局 (Floats)

浮动布局是将一个元素移动到左边或者右边, 并允许周围内容将它包围起来的方式. 使用浮动布局的方式是给一个元素设置CSS的float属性

.item {
    float: left;}复制代码

给一个被夹在一行文字中间的元素设置浮动是没有任何效果的. 如果把一个元素设置成浮动, 并且紧跟这个元素的是一个设置了背景颜色的文字块, 那么这个背景色也会出现浮动元素后面, 并且被浮动元素覆盖住.

此时如果想要区分开浮动元素的周围的文字, 那么需要给浮动元素添加 margin 属性, margin属性会使文字与浮动元素保持一定的距离. 浮动元素的 margin  属性还会让浮动元素本身和包含它的容器保持一定的距离.

清除浮动

一旦设置好了一个浮动元素, 它周围的元素都会让出一定的空间给这个浮动元素. 直到浮动元素占据的标准流布局高度被普通元素占满, 此时文档才会恢复成标准流布局. 如果想要避免这种情况, 就需要清除浮动.

给想要清除浮动的元素添加CSS的 clear 属性, 就会清楚这个元素的浮动, 设置 clear 属性为 left 或 right 就表示要清楚左边的浮动或是右边的浮动. 如果想要清除两边的浮动, 可以把 clear 属性设置为 both

.clear {
    clear: both;}复制代码

这个方法可以让浮动元素同级的元素显示正常, 但是浮动元素高度大于包含这个浮动元素的父级容器时, 父级元素就不能正确显示.

为了避免这个情况, 我们需要在容器内部完成清除浮动元素的动作, 才能让父级元素的重回标准流的布局. 清除浮动的方式参考刚才讲到的方式, 我们可以添加一个空div在浮动元素后面, 并给这个div设置 clear 属性, 让容器重回标准流布局.很多CMS系统会自动生成这个空div, 这也是曾经风靡一时的清浮动的hack方法.除了添加空div之外, 还可以通过设置CSS的content并将content设置clear的方式实现.

块内容 (Block Formatting Context)

另一个清楚浮动的方式是把容器设置为一个块内容(Block Formatting Context, 缩写为 BFC). 一个块内容会将所有内容都包裹在自己内部. 所以浮动元素也就不会超出块内容来显示. 实现块内容有几种方式, 最常用的方式就是给容器设置 overflow 属性

.container {
    display: flow-root;}复制代码

经典浮动布局

在新的布局方式出现前, 浮动常被用来创建列布局. 通过将多个浮动布局并行排列的方式, 可以实现删格化布局, 当然要仔细的设置浮动元素的宽度比例, 以免出现错行的问题.

现在已经不在推荐使用浮动来实现文档的布局. 但是很多旧网站依然在使用这种方法. 所以当你看到某个陈年旧站把所有元素都浮动起来了, 不要惊讶, 这是有原因的 (当网站多处使用浮动时, 就很难把所有的浮动都正确的清除掉, 所以不如让整个页面都浮动起来).

位置布局 (Positiong)

把一个元素从标准流中摘出, 可以使用通过给元素设置CSS的 position 属性的方式. 标准流中, 元素的 position 属性值是 static. 元素按照顺序一块一块的显示. 滚动网页时, 元素随着一起滚动.

修改了position属性后, 还需要设置元素的偏移量 offset, 才能够移动被设置过的元素位置. 移动的位置是相对元素本身的原始位置来计算的.

相对定位 (Relative positioning)

一个设置了 position: relative 元素, 它的原始位置和大小依然会被文档保留. 但此时这个元素增加相对原始位置进行移动的能力.

.item {
    position: relative;
    bottom: 50px;}复制代码

注意, 相对定位的元素在文档中的位置依然是原始位置, 而不是移动后的位置. 所以你需要手动处理因为相对位置变化二导致的元素重叠的问题, 如图:

绝对定位 (Absolute positioning)

一个设置了 position: absolute 元素, 会被移出文档的标准流. 这意味着它不会在文档占据任何空间. 它的位置会根据直系父级或更上层的具有 position 定位的元素来显示. 如果没有找到具有 position 定位的元素, 则会根据文档的视图位置来确定当前元素位置.

所以, 设置了 position: absolute 的元素会出现在文档的左上角, 然后我们可以通过 top, left, right, bottom 这4个参数来调整元素位置.

.item {
    position: absolute;
    top: 20px;
    right: 20px;}复制代码

通常, 我们不希望根据文档的视图来确定绝对定位的元素位置, 解决的办法是给绝对定位的父级元素设置 position 属性, 此时的属性值只要不是 static , 那么它内部的绝对定位子元素就会根据这个父级元素的位置来确定偏移量. 

固定定位 (Fixed positioning)

一个设置了 position: fixed 的元素会被移出文档的标准流, 并且它的位置是相对视窗可是区域来定位的. 也就是说, 当页面滚动时, fixed 定位的元素不会跟随页面一起滚动.

.item {
    position: fixed;
    top: 20px;
    left: 100px;}复制代码

这在给页面添加导航时非常有用, 无论用户如何滚动页面, 导航始终处于可视区域.

固定定位的元素并非一定是根据视图位置定位的. 在HTML中, 当固定定为的父级元素中有被设置了 transform , perspective 或 filter 属性的元素存在, 那么固定定位元素就会相对这些元素的位置来对自己进行定位. 这是因为在元素一旦被添加了3d属性后, 就会形成一个单独的重回(repain)区域, 这个区域内的绘制会独立于整个页面, 这种机制有利于利用减少也没上不必要的重绘动作并部分利用到硬件加速机制, 以此优化页面的重绘效率.

黏性定位 (Sticky positioning)

一个设置了 position: sticky 的元素, 正常情况下在文档中的占据的位置和标准流是一样的, 但是当元素达到了一个指定位置时, 该元素就会黏到一个与视图窗口固定的位置, 表现得如同固定定位(fixed positioning). 这个新增的属性已经得到了浏览器的广泛支持. 当浏览器不支持这个属性的时候, 元素会平稳退化成为一个符合标准流布局的普通元素. 所以放心的使用吧.

.item {
    position: sticky;
    top: 0;}复制代码

这个属性特别适合用来创建导航类元素, 当页面滚动超过一定位置, 导航元素就会固定在窗口的某一位置.

弹性布局 (Flex layout)

弹性布局(Flexbox)是一种一维布局, 一维布局的含义是, 我们只能在行或者列的方向来进行布局. 使用弹性布局需要给元素设置 display 属性

.container {
    display: flex;}复制代码

弹性布局的直接子元素会成为弹性元素, 这些子元素会按照指定边缘依次排成一行.

弹性布局的坐标轴 (The axes of flexbox)

在上面的例子中, 元素的一次排列是从某一个边缘开始的, 而不是固定的从左到右. flex-direction 属性用来设置flex布局的排列方向, 这个属性的默认值是 row , 也就是按行排列, 然后浏览器会根据当前的阅读方向来设置是按行的从左到右还是从右到左. 中文的阅读顺序是从左到右的, 所以默认情况下, flex布局就是按行从左到右的排列. 因此 flex-direction 就是用来定义flex布局的坐标轴方向的, 是按行还是按列来排版.

在也没的布局实践中, 熟练掌握 flex 布局的方向属性, 会让我们轻松实现多种复杂布局.

弹性布局的方向和顺序 (Direction and order)

Flex 盒子还提供了在布局坐标轴方向上的反向功能. 给 flex-direction 属性设置 row-reverse 或 column-reverse 值就可以实现在行或列方向上的逆序排列.

想要指定元素排列顺序, 还可以给元素添加 order 属性, 但是使用 order 属性时需要十分小心. 因为当用户使用键盘操作或触屏设备的时候 order 也会改变默认的顺序.

弹性布局的其他属性 (The flex properties)

跟 flex 相关的控制子元素大小的还有3个属性

  • flex-grow

  • flex-shring

  • flex-basis

这三个属性可以通过设置 flex 属性来一次性全部设置(CSS中常用的属性缩写方法). 按顺序, 被设置的属性依次是 flex-grow, flex-shrink , flex-basis

.item {
    flex: 1 1 200px;}复制代码

flex-basis 属性给 flex 元素设置初始的基本大小. 元素的增加(growing)和缩小(shrink)都会在初始大小基础上做变化. 上面的代码例子上, 元素基本大小是 200px , 所以 .item 类的元素初始大小都是 200px . 但这只是初始化的大小, 元素还会根据容器的大小自动变形, 否则当容器大小与多个 200px 元素的大小总和不匹配时, 就是会出剩余的空白或显示不全. 被设置了 flex 属性的子元素的自动变形时由 flex-grow 和 flex-shrink 来控制的.

flex-grow 属性设置为正整数时, 子元素会自动占用基本大小之外的空白区域, 直到铺满父级容器. 在上面的例子中, 元素在 200px 的基本大小基础上, 会增加每一个子元素的宽度, 来充满整个父级容器.

flex-shrink 属性设置为正整数时, 子元素会自动缩小自己的大小, 直到全部子元素刚好能全部显示在父级容器中. 这种情况会出现在父级容器无法包含设置了基本大小的子元素的时候.

flex-grow 和 flex-shrink 也可以设置为任意正整数, 在子元素自适应的过程中, 数值设置较大的元素会根据比例增加/减少更大的空间. 也就是所有子元素根据自己的 flex-grow / flex-shrink 的比例调整变化幅度. (推荐的做法是, 以1为单位, 所有子元素倍数增加/减少, 否则多种缩放比例会导致很难计算)

理解弹性布局(flexbox)的关键就是理解 flex 的3个属性的用法. flex 允许我们在一个维度上灵活的实现各种布局, 但是当我们的布局需要同时控制行和列这两个维度时, flex 就捉襟见肘了. 二维布局的最佳方式是使用 Grid 布局.

网格布局 (Grid Layout)

CSS网格布局的设计目标就是实现行和列两个维度的布局. 和弹性布局(flex)的声明方式相同, grid 布局的声明也是通过吧 display 属性设置为 grid. 同时还可以设置行和列两个维度上的基本参数, 构成一个简单完整的 grid 布局.

.container {
    display: grid;
    grid-template-columns: 200px 200px 200px;
    grid-template-rows: 200px 200px;}复制代码

上面的CSS创建了一个固定行列的 grid 布局. 但我们需要的可能不是固定行列的, 而是需要自动增加行列的布局. 这时我们不设置 grid-template-rows / grid-template-columns , 它们会使用默认值 auto 就可了. 通常的做法是, 设置固定的列数, 动态的增加行数, 这也是Excel中常用的做法.

行和列的宽度设置除了CSS的多种长度单位和百分比之外, 还有一个新增的 grid 布局单位 fr. 一个 fr 表示的是一个弹性单位, 表示在 grid 的容器中的一个自动适配后的宽度或长度.

grid 布局会为子元素自动分配空间, 不需要我们手动设置. 下面的例子中, 我们通过 fr 设置了列的款, 然后允许行高自动适配. 还通过 grid-gap 属性设置了每个子元素之间的间距距离.

grid布局的 fr 单位如何 flex-grow / flex-shrink 属性一样, 会根据设置的 fr 的大小来按比例分配容器的空白空间或按比例缩小以适应容器的狭窄空间. fr 单位还可以同其他固定长度单位混合使用 (px, em 等单位). 混合使用时, fr 会先把固定长度从父级容器中去掉, 在自动分配剩余的空间.

网格布局术语解释(Grid terminology)

网格布局包含2个维度方向, 一个是行内轴的方向, 用来标记快内的文字方向. 一个是块本身的排列方向. 

被设置了 display: grid 属性的元素就是一个网格布局的容器. 然后通过 grid-template-columns / grid-template-row 这两个属性可以创建行和列, 进而划分出网格布局的网格线. grid 布局中的最小单元是一个网格线划分出来的单元格(Grid cell), 多个单元格何在一起形成一个网格区域(Grid area). 参考下面的示例

网格线的分布

网格列的分布

网格最小单位 单元格 和有多个单元格形成的 网格区域

网格单元格自动分配 (Grid auto placement)

一旦创建 grid 布局, grid下面的直接子元素就会根据自动排版规则来进行定位, 如果对特定子元素声明了指定的排版规则, 那么指定元素会根据特定规则排版, 自动排版的元素会为声明排版的元素让出/补充相应空间.

下面的例子中, 我们将容器声明为 grid 布局, 然后对容器下的直接子元素, 每隔3个元素就声明一个占据了2行单元格的特殊排版. 其它元素不添加特定属性, 也就是其它元素适用自动排版的规则.

网格布局的基线定位 (Basic line-based positioning)

grid 布局中最简单的用法是基线定位. 对一个子元素声明它的行和列各自的启示和结束位置. 例如: 我们有一个2行3列的 grid 布局容器. 我们此时可以将一个子元素占据的空间声明为: 从第1列开始, 到第3列前结束, 从第1行开始, 到第3行前结束. 这个子元素将会占据4个基本单元格. 代码如下

.item {
    grid-column-start: 1;
    grid-column-end: 3;
    grid-row-start: 1;
    grid-row-end: 3;}复制代码

可以用另一种简洁的语法来表示相同的含义

.item {
    grid-column: 1 / 3;
    grid-row: 1 / 3;}复制代码

主动声明布局的子元素可能会出现占用同一个单元格的情况, 这是这些元素会产品重叠后的覆盖. 覆盖规则与CSS默认的元素覆盖规则相同, 即后面的元素覆盖前面的元素. 当然, 也可以通过 z-index 属性来控制元素的覆盖层级.

grid 中出现了元素的覆盖和非自动排版, 代码如下:

.one {  grid-column: 1 / 4;  grid-row: 1;  background-color: rgba(111,41,97,.3);  border: 2px solid rgba(111,41,97,.5);}.two {  grid-column: 1 / 3;  grid-row: 2;  background-color: rgba(111,41,97,.3);  border: 2px solid rgba(111,41,97,.5);}.three {  grid-column: 2 / 4;  grid-row: 2 / 5;  background-color: rgba(193,225,237,.3);  border: 2px solid rgba(193,225,237,.5);}.four {  grid-column: 1;  grid-row: 4 ;  background-color: rgba(193,225,237,.3);  border: 2px solid rgba(193,225,237,.5);}.five {  grid-column: 3 ;  grid-row: 4 / 5;  background-color: rgba(111,41,97,.3);  border: 2px solid rgba(111,41,97,.5);}复制代码

网格布局的名称定位 (Positioning with named area)

grid 布局还可要适用一种神奇的方法, 通过 grid-template-rows 属性实现的基于区域名称的定位.

.item1 {
    grid-area: a;}.item2 {
    grid-area: b;}.item3 {
    grid-area: c;}.container {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr 1fr;
    grid-template-areas: 
     "a a b b"
     "a a c c";}复制代码

适用基于名称的定位, 需要记住这几条规则. 

声明一个使用多个基本单元格的区域需要重复相同的命名. 

声明的网格区域必须是一个完整的矩形, 不能适用 L 形状或其它俄罗斯方块里才会出现的形状.

网格区域的表示需要完整的逐个单元格的标记, 如果某个单元格是空白的, 可以使用 符号点(.) 作为占位符.

.container {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr 1fr;
    grid-template-areas: 
     "a a b b"
     "a a c .";}复制代码

给予命名定位的方式可以直观的表达一个网格布局的区域分布

.container {  width: 500px;  border: 5px solid rgb(111,41,97);  border-radius: .5em;  padding: 10px;  display: grid;  grid-template-columns: 1fr 1fr 1fr;  grid-auto-rows: minmax(50px, auto);  grid-gap: 20px;  grid-template-areas:     "a a a"    "b c c"    ". . d"    "e e d"}.one {  grid-area: a;}.two {  grid-area: b;}.three {  grid-area: c;}.four {  grid-area: d;}.five {  grid-area: e;}复制代码

视觉和文档的顺序

文章开头曾建议大家整理好文档的顺序, 这样非常有利于阅读和排版. 在 flex 和 grid 的简短介绍中, 我们发现文档顺序和显示顺序可能完全不同. 这容易引起一些潜在的问题.

浏览器读取代码的顺序是按照文档的顺序进行的, 同样某些阅读器读取顺序也是按照文档顺序, 而非视觉顺序. 这种情况下, 在不支持CSS的设备中就会出现错误的问题顺序.

当你在调整元素的显示顺序的时候, 一定要想清楚, 仅仅通过CSS重新排版的顺序是否足够, 是否应该回头重新组织文档的结构. 在完成布局的页码通过tab按键测试一下页码的元素顺序, 看看文档顺序和显示顺序是否已经一致.

元素盒子的生成 (Box generation)

网页上的每一个元素都会创建一个布局上的盒子, 本篇文章描述的就是我们如果通过CSS来布局这些盒子. 但是在特定情况下, 我们可能不希望某个元素生成一个盒子. 这时我么可以使用 display 属性的两个值来完成.

display: none , 不创建盒子或内容. 

这个设置会隐藏掉元素自身和它的所有子元素及内容. 并且不会生成盒子或占据文档流中任何位置. 

.item {
    display: none;}复制代码

display: content , 只显示内容, 不创建盒子

这是CSS3新增的属性, 设置这个属性的元素, 不会生成元素自身的盒子, 但是它的所有子元素的盒子和内容都会正常生成和显示. 这个属性有助于在 grid 布局中, 让二级子元素直接参与到 grid 布局当中来. 

元素对齐 (Alignment)

元素对齐一直都使用着一些类似小窍门的方法来实现, 但这些方法都有一些不足. 直到现在, 盒子对齐模块 (Box Alignment Module) 的出现, 对齐方法才有了标准统一的方法. 目前只有 flex 和 grid 布局才使用对齐属性, 以后其它布局也会开始支持这些属性. 盒子的对齐属性列表有以下这些

  • justify-content

  • align-content

  • place-content

  • justify-items

  • align-items

  • place-items

  • justify-self

  • align-self

  • place-self

  • row-gap

  • column-gap

  • gap

速度根据布局的方式不同, 对齐属性的显示效果会有微小的差别. 我们用 grid 和 flex 作为例子看一下对齐属性的效果。

HTML代码

CSS代码

元素对齐的属性能够用来实现各种对齐方式, 但是因为兼容性问题, 目前还没普及. 但在未来, 必定成为CSS布局中的重要基石.

单元格之间的缝隙 (Row and column gaps)

多列布局中有一个列见缝隙的概念 (column-gap) . 在 grid 布局中, 也有 column-gap / row-gap /gap 这三个属性可以用来设置单元格之间的缝隙大小. 新版规范中, 推荐使用元素对齐的属性来设置包括单元格之间间隙的所有与对齐有关的属性.

多列布局 (Multi-Column Layout)

多列布局, 是一种将页面划分成多个列的布局方式. 这种方式在报纸上经常使用, 沿着划分好列的区域自上向下阅读, 读到底部后再跳转到另一列的顶部继续阅读. 这种阅读方式在网页中适合小区域的布局, 如果整个页面全部采用这种阅读方式, 用户会很反感频繁的上下滚动页面.

设置多列布局的列宽度 (Settign a column width)

在CSS中给 column-width 属性设置一个理想的宽度, 并让浏览器尽可能的按照多列模式显示.

.container {
    column-width: 300px;}复制代码

然后浏览器会尽可能创建多个列, 并且别最后不足 300px 的宽度平分给每个列. 所以如果也没宽度不是 300px 的整数倍, 那么最后生成的列的宽度会大于 300px 这个预设值.

设置多列不的列数 (Setting a number of columns)

实现多列布局, 除了设置单列宽度之外还可以设置列的数量. 浏览器会生成指定个数宽度相同的列.

.container {
    column-count: 3;}复制代码

如果同时设置了列宽和列数, 那么列的最大个数不能大于 column-count 的值. 如果有剩余空间, 将被平分到每列上.

.container {
    column-width: 300px;
    column-count: 3;}复制代码

多列之间的缝隙规则 (Gaps and column rules)

多列布局中, 每个列不能单独设置 margin 和 padding . 需要设置 column-gap 来给每个列之间留白. 在多列布局中 column-gap 的默认值是 1em, 其他布局中默认值是 0.

column-rule 属性允许在两列之间添加一些简单样式.  column-rule 是 column-rule-width , column-rule-color, colum-rule-style 三个样式的简写, 就像 border 属性也是三个属性的简写一样. column-rule 本身不复用列之间的空间. 所以列之间的真实宽度应该是 column-gap 加上 column-rule . 

跨列元素 (Allowing elements to span columns)

通过 column-span 属性, 可以将元素设置为可以跨列展示.

h3 {
    column-span: all;}复制代码

设置了 column-span 属性的元素, 本质上是打断了多列布局 的, 多列布局会在跨列元素处终止多列显示, 并在跨列元素结束后重新建立一个多列布局.

column-span 属性目前只有两个可选值: all / none. 也就是说这个属性不能指定跨列的数量.

显示内容的碎片化 (Fragmentation)

译注:  内容的碎片化指的是, 文字图片表格区块等等内容在没有足够的显示区域的时候是如何分割的. 这种分割受多种条件影响. 会产生不完整的内容碎片.

多列布局其实是碎片化的多种方式之一. 即把整块区域分割成几个列. 这种方式予与页面打印过程内容的换行方式非常像. 碎片化的过程会遵循碎片化规范, 我们也可以手动设置一些属性来指定碎片化方式.

举例来说, 在多列布局中我们需要显示一组卡片, 当卡片大小和列宽度不一致的时候, 我们希望在一个卡片内部不要出现被分割的情况. 这时可以设置 break-inside 属性来避免这种情况发生. 因为浏览器的兼容性原因, 可能还需要同时设置 page-break-inside 属性.

.card {
    page-break-inside: avoid;
    break-inside: avoid;}复制代码

如果想要避免一个标题后面被换行或分割, 还要设置 break-after 属性

.container h2 {
    page-break-after: avoid;
    break-after: avoid;}复制代码

在打印模式或多列布局中都可以使用这些属性设置. 下面例子中, 在多列模式下有3个段落. 在给 p 标签设置了 break-inside: avoid 属性后, 3个段落就不会被分割在不同的列中.

样式文件

总结

如何选择布局方式 (Selecting Layout Types: How To Choose?)

大部分网页会使用多种布局的混合模式. 并且会定义多种布局之间是如何互相影响的. 例如我们可以使用 grid 布局一整块内容, 但是在某一个单元格内使用 flex 布局. 根据要显示的内容来选择合适的布局方式. 没有最完美的, 只有最合适的. 在这篇文章中, 对基本的布局方式进行了笼统的介绍, 希望能够在CSS的布局选择上帮助大家.

总之, 不要害怕使用不同的方式来实现预想的布局效果. 新布局方法引起的问题在实际情况中不会像预想的俺么糟糕. 整理还文档的原始结构. 小心的对应视觉顺序和文档真是顺序, 剩下就是测试一下浏览器的兼容性问题了.


订阅号: webfrontend