大家好,我是树哥。
最近想着学习点前端知识,于是就学习了关于 Web 前端的布局知识,其实就是 CSS 那些事。关于 CSS 其实很早就接触过了,但一直没有沉下心来去学习,所以对于 CSS 布局的东西一直都不成体系。这次趁着重学前端,真正花时间学了一下 CSS 布局的知识点,顺带把知识点总结一下。
前言
说到 CSS 布局,有写过一些 CSS 页面的同学脑海中可能会浮现一些字眼,例如:float、display、relative、absolute 等等。但这些属性分别代表什么意思,它们之间都有什么区别,啥时候用 float 啥时候用 relative,你弄得懂吗?对于我来说,我没弄懂,有点懵。于是,我花了点时间弄懂它,这也是本文要重点弄懂的问题。简单来说,看完这篇文章,你应该可以弄清楚如下几个问题:
1、常用的几个 CSS 布局属性作用及区别。
2、CSS 布局的历史以及当前流行的布局方式。
要注意的是,本文不会从零开始介绍 CSS 的知识点。只适合学习过 CSS,但是对 CSS 布局各种属性没弄明白的同学。如果你还没学过 CSS 知识,那需要先去学习一下 CSS 基础知识再来看这篇文章。
关于文档流
理解文档流对于我们掌握 CSS 布局非常重要。简单来说,我们在 HTML 中写入的每一个元素,都是一个元素块。默认情况下,它们按照我们在 HTML 中书写的顺序,从上到下、从左到右排列,这就是默认的文档流。例如,对于如下所示的代码片段,其在 HTML 中会按照顺序显示,如下图所示。
<body>
<p>段落1</p>
<p>段落2</p>
<p>段落3</p>
</body>
核心 CSS 属性
在 CSS 布局中,有三个常用的 CSS 属性,分别是:display、float、position。它们具有不同的功能,适用于不同的场景。
display
就像 display 的名字一样,其用来定义元素块的展示形式,不同的展示形式会有不同的展示效果。 display 属性的常用属性有:
- inline:表示元素是行内元素,多个元素会共用一行。
- inline-block:表示元素是行内块元素,多个元素会共用一行。与 inline 的区别是,inline-block 元素可以设置元素的长和宽,但是 inline 元素不可以设置元素的长和宽。
- block:表示元素是块元素,每个块元素会单独占用一行。
要注意的是,不同的 HTML 元素,其默认的展示形式是不同的。例如 p 元素(段落)的 display 属性默认值是 block,而 a 属性(链接)的 display 属性默认值则是 inine。
下面,我们通过几个简单的例子来体会一下上面所说的内容。如下图所示的代码,我们设置不同的 CSS 属性,元素的展示形式会发生变化。
<body>
<p class="display">段落1</p>
<p class="display">段落2</p>
<p class="display">段落3</p>
</body>
设置的 CSS 属性如下所示:
.display {
background-color: red;
}
显示效果如下图所示。
如上图可以看到,在 CSS 代码中,我只是设置背景颜色。由于 p 元素的默认 display 属性值是 block,因此每个段落都会占用一行的空间。
如果我们把 p 元素设置成 inline 显示形式,那么它们就会多个元素排列在一行内。如下图所示。
.display {
display: inline;
background-color: red;
/* width/height 属性设置无效 */
width: 200px;
height: 200px;
}
如果我们把 p 元素设置成 inline-block 显示形式,并且设置了宽高,那么它们就会多个元素排列在一行内,并且宽高设置会生效。如下图所示。
.display {
display: inline-block;
background-color: red;
/* width/height 属性设置无效 */
width: 200px;
height: 200px;
}
看到这里,相信大家应该可以弄清楚 display 属性的作用了。display 属性其实就是用来设置 HTML 元素的展示形式的,不同的展示形式会有不同的展示效果。给不同的元素设置合适的属性值,可以帮助我们更好地进行页布局。
display 属性除了前面说得这三种属性值之外,还有 flex、grid、table 等值。但目前用得最多的还是 flex 和 grid 这两种,它们可以说是目前主流的 CSS 布局方式。关于这块内容,我们后面再细讲,这里就不展开了。
float
就像 float 这个名字一样,它代表着浮动。
啥意思呢?
要理解这个,就要从 CSS 的历史说起了。很早之前,display 属性只有两个,分别是 block 和 inline。block 虽然支持设置宽高,但是不支持多个元素显示在一行。inline 虽然支持多个元素显示在一行,但是却不能设置宽高。但是实际场景中,我们很多时候需要做多列布局的,即需要多个元素在同一行,并且同一行的元素都可以设置宽度,如下图所示。
这时候 CSS 就满足不了我们的诉求了!
那怎么办呢?
这时候 float 就横空出世了!
简单来说,float 就是让块级元素(block元素)浮起来。 块级元素浮起来之后,块级元素就不固定占用一行了,而是根据其设置的宽度显示。如果一行的宽度能够容纳得下两个浮动的块级元素,那么它们就可以同时显示在同一个行内。
举个简单地例子,下面的 HTML 片段,设置了三个 block 元素块。
<body>
<p class="display">段落1</p>
<p class="display">段落2</p>
<p class="display">段落3</p>
</body>
.display {
display: block;
width: 200px;
height: 100px;
background-color: red;
}
在没有设置浮动之前,每个块级元素都会占用一行,如下图所示。
但是如果我们对元素设置了向左浮动,那么它们就会往左浮动,三个块级元素都浮动到了同一行,如下图所示。
.display {
display: block;
float: left;
width: 200px;
height: 100px;
background-color: red;
}
所以,float 元素的出现,是用来解决 block 元素块无法同行显示,从而无法实现特定布局场景的问题的。 在 float 出现的很长一段时间,基本上大家都靠 float 来进行页面布局。
有同学会问:好像 inline-block 也能实现这个效果呀?没错,inline-block 也能实现这样的效果。但实际上,inline-block 是在 float 之后才出现的。 我猜,是 CSS 官方觉得:好像确实需要有这么一个属性值,可以让多个元素显示在同一行,又可以设置它们的宽高。人民群众既然需要,那么我们就搞一个 inline-block 给大家用吧!
但从回顾过去,貌似大家用 float 更多一些,用 inline-block 更少一些。为啥呢?或许是 inline-block 出现之前,大家都习惯用 float 了。而 inline-block 比起 float 貌似没什么太大的改变,于是就没动力去换了吧。
后来 CSS3 的 flex、grid 出现了,CSS 才真正有了一个非常好用的布局工具。到了 2023 年的今天,除非是一些需要兼容古老浏览器版本的页面需要用 float 布局,其他大多数的 Web 页面布局都使用 flex、grid 进行布局了。
看到这里,信息量貌似有点大,怎么去理解 block -> float -> inline-block -> flex/grid
的这种布局变迁呢?知乎某前端大 V 贺师俊的理解,我觉得很好:
言归正传,CSS1时代的网页还很简陋,但是随着万维网的迅猛发展,Web界面也迅速进化,当初简单的如同书页般的通栏式网页迅速绝迹,frameset由于天生存在的一堆问题也很快退出主流,这时CSS在GUI布局方面就显出了缺陷,开发者被迫使用各种trick。比如历史悠久的table布局。后来table布局被鄙视,开发者逐渐转向了float布局。
要说float布局之所以流行,IE“功”不可没。在IE中,has layout的元素是不会环绕float元素的(因为has layout的元素自己是一个控件,所以总是保持一个矩形区域)。这本来是一个bug,但是其效果却正好符合常见的双栏布局的需要。另外IE下float元素会自动撑开其父级container元素(当然前提是container元素也是has layout的),这其实也是bug,但是也恰好符合模块布局的需求。后来所谓inline-block布局其实正是这些bug的合理化。
站在今天回望过去十多年的CSS实践,我们可以发现,无论float布局还是后来的inline-block布局,其实都是trick。所谓trick,就是将一些特性挪作他用,以很曲折的方式实现出想要的效果。CSS作为样式语言,其可维护性的最终来源,就是代码能清晰的表达出设计意图。而CSS trick当然不能很好的满足这一点。
简单来说,这样的布局方式变化,其实是 CSS 不断完善进化的结果。一开始的时候,CSS 的功能比较简陋,所以需要我们自己用各种 trick 来实现需要的功能。到了后面,各种应用场景日趋完善,CSS 也不断完善起来,最终我们可以用很简单的 flex、grid 就实现之前所需要的效果。
以上关于 CSS 变迁的理解,来自于贺师俊的知乎回答,感兴趣的同学可以点击查看原文:在 CSS 中,用 float 和 position 的区别是什么? - 贺师俊的回答 - 知乎
position
如 position 名字的意思一样,position 主要是用来调整元素位置用的。一般情况下,我们用 display 和 float 做好布局之后,可能需要对元素做一些微调,那么这时候就该 position 登场了。对于 position 来说,其有五个属性值,分别是:static、relative、absolute、fixed、sticky。
static
static 关键字指定元素使用正常的布局行为,即元素在文档常规流中当前的布局位置。
如下图所示的 HTML 片段,我们不设置 position 属性,或者设置 position 属性为 static,其展示形式都不发生变化。
<div class="parent">
<div class="box"></div>
</div>
.parent{
width: 200px;
height: 200px;
border: 1px solid red;
}
.box {
position: static;
width: 50px;
height: 50px;
background-color: black;
}
relative
relative 表示相对定位,即相对于其父级容器做偏移。偏移位置使用 left/right/top/bottom 属性来设置。就如上面的例子中,如果我们使用如下的 CSS 设置,我们可以看到对应的块元素相对父容器做了偏移,如下图所示。
.parent{
width: 200px;
height: 200px;
border: 1px solid red;
}
.box {
position: relative;
left: 20px;
top: 20px;
width: 50px;
height: 50px;
background-color: black;
}
absolute
absolute 表示绝对定位。元素会被移出正常文档流,并不为元素预留空间。通过指定元素相对于最近的非 static 定位祖先元素的偏移,来确定元素位置。绝对定位的元素可以设置外边距(margins),且不会与其他边距合并。
如下所示的 HTML 片段,我们使用如下的 CSS 设置进行设置,那么对应元素块(box类所在元素)的偏移原点就不是其父级元素(son类所在元素),而是最顶层的非 static 定义的祖先元素了(parent类所在元素),如下图所示。
<body>
<div class="parent">
<div class="son">
<div class="box"></div>
</div>
</div>
</body>
.parent{
position: relative;
top: 50px;
left: 800px;
width: 300px;
height: 200px;
border: 1px solid red;
}
.son {
top: 30px;
left: 30px;
width: 100px;
height: 100px;
border: 1px solid black;
}
.box {
position: absolute;
left: 20px;
top: 20px;
width: 50px;
height: 50px;
background-color: black;
}
fixed
fixed 也表示绝对定位。元素会被移出正常文档流,并不为元素预留空间,而是通过指定元素相对于屏幕视口(viewport)的位置来指定元素位置。元素的位置在屏幕滚动时不会改变。其与 absolute 的区别是,fixed 是相对于屏幕 viewport 做偏移的,而 absolute 是相对于最近的一个非 static 祖先元素做偏移的。
如下所示的 HTML 代码块,其与上面 absolute 属性里的代码块完全一致,我们只是将 box 类的 position 属性值改为了 fixed,如下代码所示。
<body>
<div class="parent">
<div class="son">
<div class="box"></div>
</div>
</div>
</body>
.parent{
position: relative;
top: 50px;
left: 800px;
width: 300px;
height: 200px;
border: 1px solid red;
}
.son {
top: 30px;
left: 30px;
width: 100px;
height: 100px;
border: 1px solid black;
}
.box {
position: fixed;
left: 20px;
top: 20px;
width: 50px;
height: 50px;
background-color: black;
}
其展示的效果如下图所示。
从这里我们可以较为清晰地看出 absolute 和 fixed 两个属性值的区别。
sticky
sticky 表示粘性布局,其可以被认为是相对定位和固定定位的混合。元素在跨越特定阈值前为相对定位,之后为固定定位。例如:
#one {
position: sticky;
top: 10px;
}
上面的代码表示:在 viewport 视口滚动到元素 top 距离小于 10px 之前,元素为相对定位。等到距离小于 10px 之后,元素将变为 fixed 定位,元素将固定在与 viewport 顶部距离 10px 的位置。直到元素与 viewport 顶部的距离再次大于 10px,将再次变成相对定位。
一般情况下,这个用于一些滚动查看文本时,需要将某些信息置顶再顶部的情况,如下图所示。
在 sticky 属性之前,我们需要自己做很复杂的设置才能实现这样的效果。但 sticky 属性直接帮我们实现了,非常方便。
CSS 布局解决方案
看到这里,我们基本上把 CSS 布局所需要了解的知识点都介绍了一遍。那我们在实现 Web 页面的时候,到底应该用哪些 CSS 属性呢?是 float + block,还是 inlien-blcok,亦或是 flex 呢?
这里我直接给出答案:如果没有历史负担,不需要去兼容老版本浏览器,那么直接上 flex/grid 布局。如果要兼容古老的浏览器版本,那么就先用 float,float 解决不了就用 position。
为啥是这样呢?以为 flex 和 grid 布局是最新的 CSS3 提供的解决方案,是对之前 float + display + position 的总结,是更好的工具。但缺点也明显,就是一些老版本浏览器不兼容,没法使用。因此要兼容老版本浏览器的话,就只能用老古董的 float 这种 tricks 了。
float 布局方式
如果你需要用 float 这种方式去做布局,那可以参考一下这篇文章:【CSS】CSS布局解决方案(终结版) - 掘金。文章里列举了不少布局方式,还是比较实用的,让你快速掌握常用的布局方式。
我把文章中涉及到的例子都整理到了 CodePen 上,方便大家尝试,有需要的可以看看:https://codepen.io/Ronald-Chan/pen/wvRdBGL
flex 布局
对于 flex 布局来说,其使用也非常简单,基本上把对应的属性看一篇就知道怎么玩了。不像 float 布局一样,需要思来想去的,非常麻烦。
考虑到问文章篇幅和主题问题,关于如何使用 flex、grid 进行排版布局,这里就不延展展开了,后续有机会再分享 flex 布局相关内容。
总结
对于 CSS 布局,之前自己只粗浅地知道 float、display 这些属性,并没有深入对比彼此的区别。当然也没有去了解这些属性背后的 CSS 发展历程,于是很多时候都会被弄晕。
但这次通过将属性之间进行对比,再深入了解了一下 CSS 的发展历程,对 CSS 布局的知识有了整体的了解。知道过去用的是什么方式布局,现在及未来要用什么方式布局,对 CSS 布局就更有底了。
对于 CSS 布局来说,float 方式的布局慢慢会被淘汰,因此不必花大力气去学习,只在有需要的时候学习一下就好。我们的学习重点应该放在 flex、grid 等布局方式的学习,这也是我后续的学习方向。
关于 CSS 布局知识的分享就到此为止。希望这篇文章也能给你带来收获,让你更好掌握 CSS 布局技能。如果这篇文章对你有帮助,记得一键三连支持我!
参考资料
- CSS 中,position:absolute、float、display:inline-block 都能实现相同效果,区别是什么? - 一丝的回答 - 知乎
- CSS3 box-sizing 属性 | 菜鸟教程
- 官网资料!布局和包含块 - CSS:层叠样式表 | MDN
- 介绍 CSS 布局 - 学习 Web 开发 | MDN
- 【CSS】CSS布局解决方案(终结版) - 掘金