CSS – Position


前言

定位是 CSS 里蛮重要的一课.

图片黑影 (overlay), back to top button, header, footer 紧贴在屏幕上下方等效果都是靠 position 完成的.

参考:

Youtube – #CSS 認識 Position 粤语

阮一峰 – CSS 定位详解

Static

position 一共有 5 种, 默认是 static.

假设有 5 个 box

<div class="container">
  <div class="box1">box1div>
  <div class="box2">box2div>
  <div class="box3">box3div>
  <div class="box4">box4div>
  <div class="box5">box5div>
div>

它的效果就是一个一个往下放. 彼此是不重叠的.

fixed

fixed 的使用场景是在 scrolling 的时候, 我们想让某个元素一直保持在一个位置上. 比如 back to top button.

.box3 {
  position: fixed;
}

效果

当设置 fixed 以后, box3 发生了几个变化.

1.飘起来了

即便没有 scroll, 一开始 box3 就和 box4 重叠了. 这是因为 box3 飘起来了. 

这个过程类似把 box3 从布局中抽走, 整个布局变成 box1, 2, 4, 5.

2. top, right, bottom, left

通常 fixed 一定是搭配 offset 属性一起用的. 它的玩法是这样:

有 2 个对象

第 1 个是外面的 offset 对标框 (fixed 对标框就是 viewport 屏幕框)

第 2 个是里面的定位元素咯

白色区域就是整个 viewport (屏幕框)

3. offset 的默认值

当 box3 没有设置 top 偏移时, 游览器的默认行为是把它定位到它原本的位置上, 所以效果就是和 box4 发生了重叠.

如果设置 top: 60px 的话

 box3 不在和 box4 重叠, 而是和 box1,2 重叠.

注: 很少会用默认的, 而且默认的 "原地" 是依赖排版方式的, 如果是用 margin 来布局, 或者 Flex, 游览器很有可能无法正确的计算到 "原地".

absolute

absolute 和 fixed 基本上是一样的. 唯一的区别是它们的 offset 对标框不同.

fixed 的 offset 对标框是 viewport. 而 absolute 的 offset 对标框是不固定的. 

它有个术语叫 offset parent. 

通过 JS 可以拿到哦 (注: chrome position fixed offsetParent will be null. 不清楚为什么 chrome 那么特别...哈哈)

document.querySelector(".box3").offsetParent;

从 absolute element 往 parent 走, 第一个 position 不是 static 的 element 就是它的 offset parent.

虽然红框是第一个 parent 但它是 static, 所以继续往上找, 蓝框才是第一个不是 static 的 element, 所以它成为了 offset parent.

所有 offset 位置都基于它来计算.

fixed 则是不管什么 parent, 它就对着 viewport

relative

relation 和 ablsolute 就有差别了. 

第 1 它没有被抽出布局的概念.

.box3 {
  position: relative;
  top: -50px;
}

效果

先不管 top: -50px 的逻辑. 当 box3 relation 以后, box2, box4 并没有连在一起. 中间依然空了一个 box3 的距离.

这就类似灵魂出窍一样. 

第 2 它的 offset 计算不是对着 offset parent 也不是对着 viewport.

而是对着元素原本的位置.

过程类似, 在元素原本的位置画一个虚拟框作为它的 offset parent. 然后依据框做偏移. 

上面例子中 top: -50px 

红框就是元素本来的位置, -50px 往上偏移, 所以最终 box3 和 box2 重叠了.

小总结:

static: 默认 position

fixed: 会飘起来, 抽离原先的布局. 会导致原本的布局不一样. 始终和 viewport 保持固定的偏移.

absolute: 会飘起来, 抽离原先的布局. 会导致原本的布局不一样, 始终和 offset parent (第一个不是 static 的 parent) 保持固定的偏移.

最常用的手法就是给 parent position: relation 让它变成 offset parent. 因为 relation 不会对原本的布局有影响. 同时它又不是 static, 就可以成为 offset parent 了.

relation: 会飘起来, 但是不会抽离原先布局, 对原本的布局没有影响. 原地偏移. 

sticky

以前写过 

重要概念:

1. sticky scroll container

sticky element 会找到第一个 overflow auto 的 parent 作为它的 sticky scroll container (不管是 vertical 还是 horizontal, 只要能 scroll 就是 sticky scroll container).

2. sticky max area container 

sticky 的第一个 parent 就是 max area container, 没有任何要求, 第一个 parent 就是了, sticky 有没有空间就取决于这个 max area container.

3. sticky top, right, bottom, left

position sticky 一定要要配上 offset 这些会 start working.

什么时候会 stick ?

中间长方形是 viewport, 当 scroll 的时候,红色的 sticky top 会不会触发, 取决于红色 element 是否在 viewport 的前面.

蓝色的 sticky bottom 会不会触发取决于它是否在 viewport 的下面.

比如黄色在中间, 那么它是没有任何 sticky 的. 上下都不粘.

它并不完美

上面说的 1, 2 条件 sticky scroll container 和 sticky max area container 大大限制了使用的场景.

sticky 的 first overflow 很有可能是 horizontal 但是需求是更上一层的 vertical 才是 scroll container. 这就不能用了.

first parent 是 max area 更恐怖. 比如需求要做一个 animation, 你 wrap 它起来就 gg.com 了.

它适合的场景是, first parent 刚好是 max area container 同时也是 scroll container. 这样才比较顺.

可以通过 JS 实现突破这些局限. 以前没有 position: sticky 的时候大家都是这样干的. 确保性能 ok 就可以了. 它的基本原理就是做计算, 然后配上 relative or absolute 都可以 (只要定位就可以了, 其它的就是计算偏移量问题而已). 

相关