CSS定位

本文最后更新于 2025年8月2日 下午

只有绝对定位、固定定位会脱离普通文档流,静态定位、相对定位、粘性定位不会脱离普通文档流。

脱离普通文档流:从普通文档流中被移除,不影响普通文档流的布局。

1
2
3
4
5
position: static;
position: fixed;
position: absolute;
position: relative;
position: sticky;

静态定位

默认值,静态定位,没有定位,遵循正常的文档流对象。static 改成其他值,我们就说元素就被定位,是定位元素。如果元素使用静态定位,那么就说它未被定位,是非定位元素。

不会受到 top、bottom、left、right 属性的影响。

固定定位

给元素设置position: fixed就能将元素放在视口的任意位置而随着网页滑动保持不变。这样需要搭配四个属性一起使用:top、right、bottom、left 。这四个属性值决定了固定定位的元素与浏览器视口边缘的距离,还隐式地定义了元素的宽高。

固定定位让元素相对视口固定定位,此时视口被称作元素的包含块(containing block)。

定位一个元素时,不要求指定四个方向的值,可以只指定需要的方向值,然后用 width 和/或 height 来决定它的大小,也可以让元素本身来决定大小。

因为固定元素从文档流中移除,所以它不再影响页面其他元素的位置。其他的元素会跟随正常文档流,就像固定元素不存在一样。就是说它们通常会在固定元素下面排列,视觉上被遮挡。

绝对定位

绝对定位行为跟固定定位相似,只是它的包含块不一样。绝对定位不是相对视口,而是相对最近的祖先定位元素。跟固定元素一样,top、right、bottom、left 属性值决定了元素的边缘在包含块里的位置,还隐式地定义了元素的宽高。

如果父元素未被定位,则浏览器会沿着 DOM 树往上找它的祖父、曾祖父,直到找到一个定位元素用它作为包含块。但如果祖先元素都没有定位,则会基于初始包含块来定位。初始包含块跟视口一样大,固定在网页的顶部,也就是相对视口定位。

绝对定位是定位类型里的重量级选手。它经常跟 JavaScript 配合,用于弹出菜单、工具提示及消息盒子等。

相对定位

相对定位可能是最不被理解的定位类型。当第一次给元素加上position: relative时候,你通常看不到页面上有任何视觉改变。相对定位元素以及它周围的所有元素,都还保持着原来位置。

相对定位的位置依赖于文档流。如果设置 top、right、bottom、left 属性,元素就会从它原来所在的文档流位置移动,但是不会改变它周围任何元素的位置。跟固定定位盒绝对定位不一样,不能用这四个属性来改变相对定位元素的大小。这些属性只能让元素在原来的文档流位置下、左、上、右方向移动。可以用 top 或 bottom,但不能一起用( bottom 会被忽略);可以用 left 或 right,但也不能一起用( right 会被忽略)。

有时可用四个属性调整相对元素的位置,把它挤到某个位置,但这只是相对定位的一个冷门用法。更常见的用法是使用position: relative给它里面的绝对定位元素创建一个包含块。

粘性定位

粘性定位是固定定位和相对定位的结合体:正常情况下,元素随着页面滚动,当元素到达屏幕特定位置时,如果用户继续滚动,元素就会“锁定”在这个位置。这个“特定位置”,由 top、right、bottom、left 属性决定。和相对定位相似,可以用 top 或 bottom,但不能一起用( bottom 会被忽略);可以用 left 或 right,但也不能一起用( right 会被忽略),但若一个值也不设置,其定位行为与相对定位相同。

如果浏览器不支持粘性定位,通常要用固定定位或者绝对定位来回退处理。

粘性元素永远不会超出父元素的范围。注意,只有当父元素的高度大于粘性元素时侯才会让粘性元素固定,因此建议给父元素加上 min-height,以便让父元素足够高。注意,其父元素不能设置overflow:hidden或者overflow:auto属性。

定位层叠

定位非常有用,但也需要弄清楚它会带来什么后果。当把一个元素从文档流中移除时,我们就需要管理之前由文档流处理的所有事情。

首先要确保元素不会跑到浏览器视口之外,导致用户会看不到元素。其次还要保证元素不会不小心挡住重要内容。最后还有层叠的问题。在同一页面定位多个元素时,可能会遇到两个不同定位的元素重叠的现象。

渲染过程和层叠顺序

浏览器将 HTML 文件解析为 DOM 的同时还创建了另一个树形结构,叫作渲染树(render tree)。它代表了每个元素的视觉样式和位置。同时还决定浏览器绘制元素的顺序。顺序很重要,因为如果元素刚好重叠,后绘制的元素就会出现在先绘制的元素前面。

通常情况下(使用定位之前),元素在 HTML 里出现的顺序决定了绘制的顺序。但在使用定位元素时,这种行为会改变。浏览器会先绘制所有非定位的元素,然后绘制定位元素。默认情况下,所有的定位元素会出现在非定位元素前面。对于定位元素,基于源码的层叠关系并没有改变,依然按照后绘制的定位元素出现在先绘制的定位元素前面的规则。

通常情况下,模态框要放在网页内容的最后,body 标签关闭之前。大多数的构建模态框 JavaScript 库会自动这样做。因为模态框使用固定定位,所以不必关心它出现在哪,它会一直定位到屏幕中间。

改变固定定位元素的标记位置不会产生不好的影响,但是对相对、绝对或粘性定位的元素来说,通常无法用改变标记位置的方法解决层叠问题。相对定位依赖于文档流,绝对定位依赖于元素的定位祖先节点。这时候需要用 z-index 属性来控制它们的层叠行为。

z-index 属性控制层叠顺序

z-index 属性值可以是任意整数(正负都行),z 表示是笛卡儿 x-y-z 坐标系里的深度方向。z-index 较高的元素出现在 z-index 较低的元素前面。z-index 为负数的元素出现在静态定位元素后面。

使用 z-index 是解决网页层叠问题的第二个方法,该方法不要求修改 HTML 的结构。

z-index 的行为很好理解,但是使用它时要注意两个小陷阱。第一,z-index 只会在定位元素上生效,不能用它控制静态元素。第二,给一个定位元素加上 z-index 可以创建层叠上下文。

理解层叠上下文

一个层叠上下文包含一个元素或者由浏览器一起绘制的一组元素。其中一个元素会被作为层叠上下文的根,比如给一个定位元素添加 z-index 时,该定位元素就变成了一个新的层叠上下文的根,它所有后代元素就是这个层叠上下文的一部分。

不要将层叠上下文跟 BFC 弄混,它们是两个独立的概念,尽管不一定互斥。层叠上下文是负责决定哪些元素出现在另一些元素前面,而 BFC 负责处理文档流,以及元素是否会重叠。

实际上将层叠上下文里的所有元素一起绘制会造成严重的后果:层叠上下文之外元素无法叠放在层叠上下文内的两个元素之间。换句话说,若一个元素叠放在一个层叠上下文前,那么此层叠上下文里没有元素可以被拉到该元素前面;若一个元素被放在层叠上下文后,那么层叠上下文里没有元素能出现在该元素后面。

给一个定位元素加 z-index 属性是创建层叠上下文最主要的方式,但还有别的属性也能创建,比如小于 1 的 opacity 属性,以及 transform、filter 属性。由于这些属性主要会影响元素及其子元素渲染的方式,因此一起绘制父子元素。文档根节点(html 元素)会给整个页面创建一个顶级的层叠上下文。

所有层叠上下文内的元素会按照以下顺序,从后到前叠放:

  • 层叠上下文的根
  • z-index 为负的定位元素(及其子元素)
  • block 块状水平盒子
  • float 浮动盒子
  • inline/inline-block 水平盒子
  • z-index 为 auto 看成 z-index 为 0 的定位元素(及其子元素),不靠 z-index 的重叠上下文
  • z-index 为正的定位元素(及其子元素)

如果发现 z-index 没有按照预期表现,就在 DOM 树里往上找元素祖先节点,直到层叠上下文的根,然后给它设置 z-idnex,将整个层叠上下文向前或向后放。还要注意多个层叠上下文嵌套的情况。

网页很复杂时,很难判断是哪个层叠上下文导致的问题。因此在创建层叠上下文的时候就一定要多加小心,没有特殊理由的话不要随意创建,尤其是当一个元素包含了网页很大一部分内容的时候。尽可能地将独立的定位元素(如模态框)放到 DOM 的顶层,body 结束标签之前,这样就没有外部的层叠上下文能束缚它们。

有些开发人员会忍不住给页面的大量元素使用定位。一定要克制这种冲动。定位用得越多,网页就越复杂,也就越难调试。如果你定位了大量元素,就回头评估一下现在的情况,尤其当你发现很难调试出自己想要的布局时,一定要反思。如果可以用别的方法实现某个布局,应该优先用那些方法。

如果能够依靠文档流,而不是靠明确指定定位的方式实现布局,那么浏览器会帮我们处理好很多边缘情况。记住,定位可能会将元素拉出文档流。一般来说只有在需要将元素叠放到别的元素之前时,才应该用定位。


CSS定位
https://xuekeven.github.io/2021/10/03/CSS定位/
作者
Keven
发布于
2021年10月3日
更新于
2025年8月2日
许可协议