到祖先里找不粘住的原因
目标
沿着祖先向上,亲自计算包含块、滚动容器和层叠上下文。这里没有浏览器,但这三者只凭标记和 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 的范围。如果把屏幕宽度作为第二个参数传入,就会按该宽度重新计算。
步骤
- 让
.badge以.card为基准(绝对定位的包含块) - 让
.overlay以视口为基准(fixed的默认包含块) transform放在哪里——fixed被困住的陷阱- 让
.topbar成为 sticky(阈值) - 让
.toc真正粘附(选定滚动容器) - 让
z-index在根部较量(层叠上下文) - 把计算器的结果保存为
07-boxes.txt - 在窄屏幕上让目录回到文档流(再次遇到顺序陷阱)
- 总结 →
09-notes.md
参考
- 第 3 步请先在
.card上加上transform并运行计算器,亲眼看到它被困住之后,再把这个属性移到.badge上。 inset: 0这一行的意思是把四个方向都设为 0。- 粘附的区间长度取决于父盒子的高度,计算器无法得知。这必须在浏览器中查看。
建立绝对定位的参照
创建 /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。样式表也必须仍然通过前面步骤的标准。
核心只有一点——原因始终在祖先身上。 请用自己的话写下,下次遇到同样的症状时,要沿着祖先向上查找什么。