TT Lab
开始
学习 学习路径 课程

CSS — 后写的规则获胜

到祖先里找不粘住的原因

在 TT Lab 中继续学习

目标

沿着祖先向上,亲自计算包含块、滚动容器和层叠上下文。这里没有浏览器,但这三者只凭标记和 CSS 就能确定答案。

为什么重要

fixed 不固定、sticky 不跟随的缺陷,症状完全没有指向原因。这个元素的 CSS 完全正确,用开发者工具只看这个元素,不会发现任何问题。该看的地方是祖先。

只要祖先上有一个 transform,fixed 的基准就会从视口变成这个祖先。只要祖先上有一个 overflow: hidden,这个盒子就会成为滚动容器,sticky 就没有粘附的机会了。卡片悬停效果与模态框看起来毫无关系,所以如果不了解规则,找到原因要花好几个小时。

本实验的文档

评分器和计算器会假设下面的结构。不需要编写 HTML。

div.page
├ header.topbar
└ div.shell
  ├ aside.side
  │ └ nav.toc
  └ main.content
    └ div.card
      ├ span.badge
      └ div.overlay

要创建的内容

只有一个文件:/root/work/pos/layout.css。

计算器

cd /root/work/pos
python3 /opt/lab/checks/css-pos-lab/boxes.py layout.css
python3 /opt/lab/checks/css-pos-lab/boxes.py layout.css 500

它会为每个元素输出 position、包含块、sticky 判定和 z-index 的范围。如果把屏幕宽度作为第二个参数传入,就会按该宽度重新计算。

步骤

  1. 让 .badge 以 .card 为基准(绝对定位的包含块)
  2. 让 .overlay 以视口为基准(fixed 的默认包含块)
  3. transform 放在哪里——fixed 被困住的陷阱
  4. 让 .topbar 成为 sticky(阈值)
  5. 让 .toc 真正粘附(选定滚动容器)
  6. 让 z-index 在根部较量(层叠上下文)
  7. 把计算器的结果保存为 07-boxes.txt
  8. 在窄屏幕上让目录回到文档流(再次遇到顺序陷阱)
  9. 总结 → 09-notes.md

参考

建立绝对定位的参照

创建 /root/work/pos/layout.css,把 .badge 设为 position: absolute,并设置两个以上的方向(例如 top 和 right)。它的包含块必须是 .card。

mkdir -p /root/work/pos。absolute 的包含块是最近的已定位祖先。relative 会让自己留在原位,同时只建立一个基准,所以最常被用于这个目的。

把遮罩固定在视口上

把 .overlay 设为 position: fixed,并确定全部四个方向(用一行 inset: 0 也可以)。也要设置 z-index。包含块必须是视口。

fixed 的默认包含块是视口——所以滚动时它不会跟随,而是固定在屏幕上。四个方向都确定了,才会铺满整个屏幕。

transform 放在哪里

给 .badge 设置 transform,让它稍微越过角落之外。.card 上不能有 transform、filter、perspective、will-change、contain 中的任何一个,并且 .overlay 的包含块仍然必须是视口。

请先在 .card 上加上 transform: translateZ(0) 并运行计算器——.overlay 的包含块会变成 .card。这就是本实验想展示的陷阱。确认之后,把这个属性移到真正需要移动的元素上。

让顶栏粘附

把 .topbar 设为 position: sticky,并设置 top。计算器必须判定为“会粘附”。

如果没有阈值(top、right、bottom、left 中至少一个),就没有可以粘附的线,它只会随着文档流走。既没有错误也没有警告,这一点让这个缺陷得以长期存活。

选定滚动容器

把 .toc 设为 position: sticky; top: 64px,并给 .page 设置 overflow-y: auto,让 .page 成为滚动容器。.shell 和 .side 上不能有不是 visible 的 overflow。

sticky 只会在最近的滚动容器内粘附。如果中间的容器上有一个 overflow: hidden,它就会成为滚动容器,而用户不会去滚动这个盒子,所以就没有粘附的机会。overflow-x: hidden 也会产生同样的结果。

z-index 起作用的范围

给 .topbar 设置 z-index,并让 .overlay 的 z-index 比它更大。.page、.shell、.content、.card 中不能有任何一个创建层叠上下文。

如果祖先创建了层叠上下文,其中的 z-index 无论多大都出不去。opacity: 0.99 或 transform 这样不经意的一行就会创建它。计算器会一并输出“z-index 在哪个盒子内起作用”。

用计算器确认

运行 boxes.py,把结果保存为 /root/work/pos/07-boxes.txt。输出中不能出现“不会粘附”的判定,并且元素行必须完整、没有被截断。

python3 /opt/lab/checks/css-pos-lab/boxes.py layout.css | tee 07-boxes.txt。包含块旁边的括号会告诉你它为什么是这样确定的——是祖先的名称和它的属性。

窄屏幕上回到文档流

在 @media (max-width: 700px) 中,把 .toc 恢复为 position: static。在宽屏幕(1280px)上它仍然必须粘附。媒体查询必须放在基础规则下面。

特异性相同时,后写的获胜。在媒体查询内并不会更强——如果把基础规则放在媒体查询下面,媒体查询就什么也做不了。请用 boxes.py layout.css 500 确认窄屏幕的情况。

写下诊断顺序

在 /root/work/pos/09-notes.md 中写三行以上(120 个字符以上)。正文中必须包含 담음 블록(韩文,意为“包含块”)、transform 和 overflow。样式表也必须仍然通过前面步骤的标准。

核心只有一点——原因始终在祖先身上。 请用自己的话写下,下次遇到同样的症状时,要沿着祖先向上查找什么。