目的
定位的作用主要是将元素从文档流中移走,允许将元素放到页面上的任意位置。那么我们经常说的文档流又是什么意思呢?
文档流可以理解为一种布局机制,在文档流中的元素分为block inline inline-block,所有的这些元素按照一定的规则进行布局,脱离文档流就不会应用这些元素排布规则
固定定位
固定定位是相对于视口进行定位的,设置position:fixed
就可以放到页面的任意位置
- 如果不设置width/height, 那么设置top / bottom / left / right 一起使用,也可以设置元素的大小
- 如果设置了width/height
- 不设置四个方向的值的时候,那么默认会给固定定位的元素添加top: 0px, left: 0px, 另外两个方向的值会动态计算
- 如果同时设置了四个方向的值,那么会优先top / left生效,举个例子:
<!---->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
}
body {
height: 1600px;
background-color: skyblue;
}
div {
width: 400px;
height: 400px;
background-color: pink;
position: fixed;
top: 10px;
left: 10px;
right: 20px;
bottom: 20px;
}
</style>
</head>
<body>
<div></div>
</body>
</html>
绝对定位
绝对定位是相对于“包含块”进行定位,在固定定位中“包含块”可以看作是视口,绝对定位也是如此,只不过其包含块是最近的祖先定位元素,也是靠top / bottom / left / right 来定位
- 如果没有找到,会相对于初始包含块来定位,这个初始包含块和视口一样大,固定在页面顶部 (这里的初始包含块到底是什么意思呢?是不是body呢)
相对定位
相对定位元素是相对于元素本来在文档流中的位置定位的(脱离文档流了吗?)
- 使用top / bottom / left / right只能定位,不能设置大小,这点和固定定位和绝对定位的元素区别
层叠上下文和z-index
首先明白浏览器基本的渲染顺序是定位元素在非定位元素的前面,后面的元素在前面的元素前面。
如果想要控制层叠顺序,那么可能需要z-index,拥有较高z-index的元素出现在拥有较低z-index的元素前面。拥有负数z-index的元素出现在静态元素后面。
那么首先我们理解一下层叠上下文的概念,什么是层叠上下文呢?
- 简单解释:一个层叠上下文包含一个元素或由浏览器一起绘制的一组元素,相当于确定了内部所有元素的渲染顺序
- 但是需要注意一个问题,就是:如果一个元素叠放在一个层叠上下文前面,那么层叠上下文里没有元素可以被拉到该元素前面。
- 做了一个实验:
<!---->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
}
body {
height: 1700px;
background-color: skyblue;
}
[class^="item"] {
background-color: pink;
width: 50px;
display: inline-block;
border: 1px solid black;
height: 50px;
}
.item2 {
position: relative;
left: 20px;
top: 20px;
z-index: 1;
}
.item2 span {
width: 50px;
height: 20px;
z-index: 3;
position: relative;
display: inline-block;
background-color: blueviolet;
}
.item3 {
position: relative;
left: -10px;
z-index: 2;
top: 10px;
}
</style>
</head>
<body>
<div class="father">
<div class="item1"></div>
<div class="item2">
<span>1</span>
<span>2</span>
<span>3</span>
</div>
<div class="item3"></div>
<div class="item4"></div>
</div>
</body>
</html>
这里踩了一个坑:把.item2设置成了0.9,发现效果和预期的不一样
查了一下发现:z-index不支持小数,W3C的语法来看:z-index: auto | ; 如果是小数,会被当作auto,同时不会创建层叠上下文
其次,用什么方式可以创建层叠上下文呢?
- position: absolute / relative + z- index: auto以外的其他值
- position: fixed / sticky
- flex item / grid item + z-index : auto 以外的其他值
- mix-blend-mode:非normal
- 小于1的opacity
- transform, scale, rotate, translate, filter, backdrop-filter, perspective, clip-path, mask / mask-image / mask-border属性
- 文档根节点html
- 等等
为什么要创建层叠上下文呢?
想象网页是一本书,每个层叠上下文是一本独立的小册子:
- 你可以在小册子里随便排顺序(z-index)
- 但册子之间的顺序是固定的,不能插页
- 想改某个元素的位置,必须在它所在的小册子里动手
- 最后浏览器把这些册子叠起来,变成你看到的完整页面
那么一组层叠上下文的元素会如何放置呢?
- 层叠上下文的根(是文档流的第一块)
- z-index为负的元素
- 非定位元素(是文档流)
- z-index: auto
- z-index为正
(这里容易有个误区,以为非定位元素的z-index: auto,实际上非定位元素是没有z-index的,而设置了定位元素之后,z-index默认就是auto)
可以看作每个层叠上下文的内部都有自己的排布规则,但是整体的排序又是根据层叠上下文根的层级来定的,整体可以想象为一个嵌套结构
同时要明白创建层叠上下文不一定就是脱离了文档流,有些会有些不会
粘性定位
粘性定位可以看作是固定定位和相对定位的结合体,在正常情况下,元素随着屏幕滚动,到达一定阈值之后,就会“锁定”在屏幕的某个位置
粘性定位在我们的开发中用的也挺多的,但是好像一直对于细节不是很清楚,在开发中最多的是粘在顶部,比如这样:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
}
body {
height: 1700px;
background-color: skyblue;
}
div {
position: sticky;
margin-top: 600px;
width: 100px;
height: 100px;
background-color: pink;
bottom: 190px;
/* top: 10px; */
/* top: 20px;
bottom: 20px;
left: 20px;
right: 20px; */
}
</style>
</head>
<body>
<div></div>
</body>
</html>
设置一个top值就可以在滚动的过程中,如何发现距离视口[top值]px的时候,一直固定在某个位置了
那么如何固定的底部呢?
- 因为页面整体的滚动方向是从上往下,也就是说:浏览器默认是向上滚动的,所以浏览器会为它计算粘性触发点。所以可以把body当作一个明确的“滚动容器”
- 但是这种情况下,底部粘性是无法生效的,必须显式给一个非 body 的父容器设置 overflow: auto/scroll;然后在子元素上设置 position: sticky; bottom: 0
<!---->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
}
.container {
width: 500px;
height: 500px;
background-color: pink;
overflow: auto;
}
.item {
width: 100px;
height: 200px;
background-color: aqua;
border: 1px solid black;
}
.item1 {
width: 100px;
height: 100px;
background-color: rgb(34, 13, 111);
position: sticky;
bottom: 0;
}
</style>
</head>
<body>
<div class="container">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item item1">4</div>
<div class="item">5</div>
<div class="item">6</div>
</div>
</body>
</html>
视口概念
在之前的内容中,我们提到了是视口,初始包含块,相信大家也听说过一些其他的概念,布局视口以及理想视口等等,那么对于其中的细节一直都是一知半解,下一篇会详细介绍这里的概念~
Antd中相关组件设计
Ant Design(antd)中的组件设计非常注重可复用性和功能解耦。以 Popover
为例,它的定位实现依赖的是一个底层的 弹出层定位工具库 —— rc-trigger
,而不是直接手写定位逻辑。
这里不过多介绍,如果想调试antd源码,可以查看www.zhihu.com(虽然我并没有成功,还在尝试中)
参考文献
Stacking context - CSS: Cascading Style Sheets | MDN
《深入解析CSS》