Skip to content

页面布局原理

css的页面布局特别繁杂。写一篇文章总结一下原理,以便以后复习使用。

本文不涉及具体的属性设定和代码,只讨论其背后的设计原理。

正常流(Normal Flow)

最基础的html布局方式,自顶向下,根据盒子模型,依次排列。

此时元素被分为行内元素(内联元素),块级元素,以及行内块元素。

正常的布局流是将元素放置在浏览器视口内的系统。默认情况下,块级元素在视口中垂直布局——每个都将显示在上一个元素下面的新行上,并且它们的外边距将分隔开它们。内联元素表现不一样——它们不会出现在新行上;相反,它们互相之间以及任何相邻(或被包裹)的文本内容位于同一行上,只要在父块级元素的宽度内有空间可以这样做。如果没有空间,那么溢流的文本或元素将向下移动到新行,这将扩大父元素的实际宽高。

这就是默认的显式方式。

盒子模型:元素显示模式

块级盒子的标准定义

一个被定义成块级的(block)盒子会表现出以下行为:

  • 盒子会在内联的方向上扩展并占据父容器在该方向上的所有可用空间,在绝大数情况下意味着盒子会和父容器一样宽
  • 每个盒子都会换行
  • 宽高属性可以发挥作用
  • 内边距(padding), 外边距(margin)和 边框(border)会将其他元素从当前盒子周围“推开”

内联元素的标准定义

  • 盒子不会产生换行。
  • 宽高属性将不起作用。
  • 垂直方向的内边距、外边距以及边框会被应用但是不会把其他处于 inline 状态的盒子推开。
  • 水平方向的内边距、外边距以及边框会被应用且会把其他处于 inline 状态的盒子推开。

本质

与其说是行内元素和块级元素,不如说他们的区别就在于,一个默认向外扩张,并且推开别的盒子,因为默认撑满了整个屏幕的宽度,所以才认为“默认换行”;另一个默认向内内联,内容有多少就老老实实显示多少,默认不换行, 宽高不予设定,仅仅依赖于内容多少。

至于行内块元素,则是在内联的基础上增加了可以自定义宽高的特性,算是二者的结合中间体。

浮动(Float)

浮动被发明的初衷是用来做图文混排的。但后来发展成实现整个页面,也因此留下了很多的问题。

浮动可以用来实现整个网站页面的布局,它使信息列得以横向排列(默认的设定则是按照这些列在源代码中出现的顺序纵向排列)。目前出现了更新更好的页面布局技术,所以使用浮动来进行页面布局应被看作传统的布局方法

原理

定义

float 属性定义元素在哪个方向浮动。浮动元素会生成一个块级框,直到该块级框的外边缘碰到包含框或者其他的浮动框为止。

解读

浮动元素会脱离正常的文档布局流,并吸附到其父容器的左边(至于是左边的哪一部分,这将取决于它前面有没有什么块级元素之类的东西。)。在正常布局中位于该浮动元素之下的内容,此时会跟在浮动元素后面,填补其右侧的空间。浮动元素默认的行为类似于行内块元素。

为了便于理解,这里给出了在一个div块级元素中,几种100像素方块放置后的效果:

  • 顺序放置一个块级元素A和一个浮动元素B。效果为B出现在了A的正下方,且无缝隙。

  • 顺序放置一个行内块元素A和一个浮动元素B。效果为B跟在了A的后面,且无缝隙。

  • 顺序放置一个内联元素A和一个浮动元素B。效果为B遮住A。

可见,浮动元素跟在其他元素后面时,等同行内块元素的行为。但是,它和行内块元素不同,它并不占空间

  • 顺序放置一个浮动元素B和一个块级元素A。效果为A遮住B。
  • 顺序放置一个浮动元素B和一个行内块元素A。效果为A跟在了B的后面,且无缝隙。
  • 顺序放置一个浮动元素B和一个内联元素A。效果为A遮住B。

一些现象反思

难以预料的布局:一个比较好的提议是,不要让浮动元素和非浮动元素混在一起。因为这往往会导致意料之外的结果。我们需要遵循块级,内联的严格定义,并且与浮动的定义相结合,才能保证绝对不出错——这绝对是个伤脑筋的事情。对于这类问题,往往有更好的解决的方案。

塌陷:浮动元素本身呈现出的效果是“不占空间”,所以如果一个父盒子中只有浮动元素,并且没有指定父盒子的高度,就会造成“塌陷”,也就是父级元素的高度变成了0。此时再添加新元素在后面,效果会是被浮动元素覆盖。解决方案是一个叫做“清除浮动”(虽然我不太明白为什么要这样叫)的方法。

文字环绕特性:浮动元素本身不占空间,但是普通的文档流还是会避开它。前面讨论的全部都是元素之间相邻放置的情况,但是当浮动元素后面或者前面遇到文本的时候,它一定不会与文本层叠。这是浮动被设计成图文环绕初衷的最直接体现吧。

清除浮动

  • clear+额外标签

这种方法通过添加一个空的块级标签,让父盒子中不再只有浮动元素,并依赖该标签的clear属性让父元素达到实际高度,从而解决掉塌陷问题。

当一个元素被赋予clear属性的时候,它将必须移动到在它之前的浮动元素下面。这将间接的实现父容器的高度的改变。

注意, 该空元素必须是块级元素。如果是内联元素或者行内块元素,会出现意料之外的Bug.

  • overflow

利用了BFC特性。

  • after伪元素法

after可以创建一个标签。我们可以利用该标签,添加块级和clear属性,从而达到和方法1一样的效果。

  • 双伪元素法

定位(Position)

定位允许你从正常的文档流布局中取出元素,并使它们具有不同的行为。例如放在另一个元素的上面,或者始终保持在浏览器视窗内的同一位置。这很有用。

静态定位

静态定位的定义是“将元素放入它在文档布局流中的正常位置”。这看起来没有什么用,也确实如此。所有的元素默认情况下 position 属性都是 static。

相对定位(relative)

通过设定不同的方向像素值,让元素偏离原本应该在的位置。MDN有个形象的描述,把相对定位比喻成推开盒子的“力”。相对定位可以看作普通流的一部分,移动后的元素仍占据原来的位置,移动元素会导致他层叠其它盒子。

绝对定位(absolute)

绝对定位的位置将取决于其父容器,而不是“绝对的取决于屏幕大小”,后者是fixed定位,不要混淆。

简单来说,绝对定位的元素的位置相对于最近的已定位父元素,如果元素没有已定位的父元素,那么它的位置相对于浏览器视口。

绝对定位相对于已经定位的父级元素,它脱离文档流单独存在。行内元素进行绝对定位和固定定位(脱离文档流),都会变成块级元素(行内块元素)。

原理

如果所有的父元素都没有显式地定义 position 属性,那么所有的父元素默认情况下 position 属性都是 static。结果,绝对定位元素会被包含在初始块容器中。这个初始块容器有着和浏览器视口一样的尺寸,并且<html>元素也被包含在这个容器里面。简单来说,绝对定位元素会被放在<html>元素的外面,并且根据浏览器视口来定位。

可以通过改变定位上下文 —— 绝对定位的元素的相对位置元素。通过设置其中一个父元素的定位属性,从而让绝对定位以它为参照点。

Z-INDEX

对于多个定位层叠,显然会出现谁覆盖谁的问题。规定,在渲染时,后定位的元素将赢得先定位的元素。这个规定可以通过设置z-index优先级属性来显式的改变。

其他定位

sticky

还有一个可用的位置值称为 position: sticky,比起其他位置值要玩一些。它允许被定位的元素表现得像相对定位一样,直到它滚动到某个阈值点(例如,从视口顶部起 10 像素)为止,此后它就变得固定了。例如,它可用于使导航栏随页面滚动直到特定点,然后粘贴在页面顶部。这个定位显然非常常用。

fixed

元素的位置相对于浏览器窗口固定位置。即使窗口是滚动的它也不会移动。值得注意的一点是,fixed元素的top,bottom,left,right,只设置margin的时候,效果上呈现的是相对于最近的父元素进行定位的。所以,一般都会加个top:0。另外,如果fixed元素里面没有内容,其全部样式也会被隐藏!

弹性盒子(Flex)

弹性和网格是最重点的,也是最常用的页面布局属性。

定义

Flexible Box 模型,通常被称为 flexbox,是一种一维的布局模型。所以 flex 容器中的所有 flex 元素都会有下列行为:

  • 元素排列为一行 (flex-direction 属性的初始值是 row)。
  • 元素从主轴的起始线开始。
  • 元素不会在主维度方向拉伸,但是可以缩小。
  • 元素被拉伸来填充交叉轴大小。
  • flex-basis 属性为 auto
  • flex-wrap 属性为 nowrap

弹性盒字如其名,内部的元素都会自动的拉伸扩充,以塞满整个容器。这间接的提供了响应式布局居中效果,所以才会那么常用。

Flexbox 的一个关键特性是能够设置 flex 元素沿主轴方向和交叉轴方向的对齐方式,以及它们之间的空间分配。

网格(Grid)

这是一种比较新的布局。也是未来的趋势。网格是由一系列水平及垂直的线构成的一种布局模式。根据网格,我们能够将设计元素进行排列,帮助我们设计一系列具有固定位置以及宽度的元素的页面,使我们的网站页面更加统一。

弹性 or 网格?

实际上,二者都是很好的布局方案,各有其特点。在选择上,MDN建议:

  • 从需求考虑

    • 我只需要按行或者列控制布局?那就用弹性盒子;

    • 我需要同时按行和列控制布局?那就用网格。

  • 是内容优先还是大体布局优先?

    • 弹性盒从内容出发。一个使用弹性盒的理想情形是你有一组元素,希望它们能平均地分布在容器中。
    • 网格则从布局入手。当使用 CSS 网格时,你先创建网格,然后再把元素放入网格中。

一个短小精悍,一个顾全大局。