目录
为一个标准流块级元素添加浮动后,该块级元素就会脱标进入浮动流,在浮动流中块级元素(已转为了浮动元素)可以在水平方向上进行布局,这样我们就可以:
而这就是浮动布局。
浮动布局的缺点在于:
浮动元素无法自由地指定位置,只能进行左浮动或右浮动,而浮动后的位置是很难控制的。
所以我们需要一种可以实现自由指定元素位置的方式:定位
在CSS中:定位 = 定位模式 + 边偏移
我们通过给元素设置position样式来指定元素的定位模式,常用定位模式如下:
定位模式的作用是指定元素的定位方式
元素指定了定位模式后,需要继续使用边偏移样式来定位元素的位置,边偏移样式有:
这里无法给出边偏移的含义描述,因为不同定位模式的边偏移效果不同。
标准流中的元素都存在一个隐式的position:static样式,即标准流中的元素都是静态定位的,所谓静态定位即:
静态定位的元素不会脱标,而是就处于标准流中。
静态定位的元素使用边偏移left、right、top、bottom无效,使用z-index无效。
一般来说,静态定位就是标准流布局。
相对定位的目的是:在不改变页面布局的前提下调整元素位置。
我们为标准流中的元素添加position:relative样式后,该元素就变为了相对定位元素。
由于相对定位的元素位置改变,不能改变页面布局,所以相对定位元素需要占据着它在标准流中的位置,即不脱标,否则相对定位元素脱标,会导致它在标准流中后面的元素进据脱标位置,导致页面布局改变。

上例中,原本红色、蓝色div都是标准流中的块级元素,所以呈现垂直排列。之后将红色div变为相对定位元素,并添加边偏移,导致红色div的位置改变,并空出了原有位置,但是蓝色div并未进据空出来的位置。
因此相对定位元素并未脱标。
相对定位元素进行边偏移时,并非它在标准流中的本体的位置改变,而是本体的影子的位置改变。
影子进行边偏移的参照物就是它在标准流中的本体,所以相对定位模式下的边偏移规则如下:

上例中,蓝色元素是相对定位的,其本体位于标准流中,有margin:10px的外边距,其影子相对于本体左移10px,上移10px,刚好抵消了margin-top、margin-left的10px距离
影子其实可以理解处于”定位流“中,定位流可以理解为处于标准流、浮动流之上的图层。
所以影子会盖住标准流、浮动流中的元素。

上例中,蓝色元素是标准流中块级元素,绿色元素是浮动流中浮动元素,红色是定位流中定位元素。
说明:定位流 位于 浮动流 之上, 而浮动流 又位于 标准流之上。
定位流其实并不是单个图层,而是一个图层栈。
默认情况下,先进入定位流的元素图层 总是会被 后进入定位流的元素图层 压住。

如上例中,我在标准流中新增了一个黄色div,一开始黄色div处于标准流中,所以被相对定位的红色div的影子压住,而当我给黄色div添加相对定位后,黄色div的影子也进入了定位流,且相对于红色div的影子是后进入的,所以黄色div的影子又压住了红色div的影子。
而定位流除了支持这种后来居上的定位元素显示规则外,还支持根据定位元素的z-index样式来显示。
所有的定位元素(除了position:static外)都支持z-index,而z-index也仅在定位元素上有效,在其他非定位元素上无效。
定位元素默认隐式包含一个z-index:auto的样式,定位流对于z-index:auto的定位元素,采用后来者居上的显示规则。

当然我们可以自定义定位元素的z-index值,z-index值支持整数,包括正整数、0、负整数
当z-index设置为0时,其实和z-index:auto效果一样,也是遵从后来者居上的原则

当z-index设为正整数时,值越高,显示优先级越高,如下面例子

当z-index为负数时,表示降低显示优先级,如下例,优先级 auto > -1 > -2

并且当我们设置定位元素的z-index为负数时,标准流、浮动流图层也将优先于它显示

那么是否只要z-index越大,对应定位元素的显示优先级就越高呢?
不是的,情况下面例子:
只要父的z-index不为auto,则子的显示优先级永远高于父。

如果父的z-index为auto,子的z-index为负数,则父优先于子显示。

这是什么原因导致的呢?请看下节关于层叠上下文的介绍:
在CSS2.1规范中,每个盒模型的位置是三维的,分别是平面画布上的x轴,y轴以及表示层叠的z轴。
一直以来,我们都用标准流、浮动流、定位流来描述元素在z轴上的层叠关系,这是非常笼统的、不准确的,在CSS2.1规范中,元素的层叠关系的描述使用:层叠上下文。
什么是层叠上下文呢?
我们可以将层叠上下文理解为PS中的图层。
然而并非所有元素都能够形成层叠上下文,常见的形成层叠上下文的条件如下:
position值为 absolute|relative,且 z-index值不为 autoposition 值为 fixed|stickyz-index 值不为 auto 的flex元素,即:父元素 display:flex|inline-flexopacity 属性值小于 1 的元素transform 属性值不为 none的元素层叠上下文中可以继续形成子层叠上下文,比如下面例子

html根元素形成了一个根层叠上下文,red元素又在根层叠上下文的基础上形成一个子层叠上下文。
所以一个层叠上下文(如上例中html根元素)中会包含两类元素:
此时我们就需要关注如下层叠关系:
一个层叠上下文中各元素层叠优先级如下:

上图中将层叠上下文中元素做了较为详细的划分:
、z-index为0的元素、z-index正值的元素从上图中可以分析出,
我们通过几个例子来验证下:
- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Documenttitle>
- <style>
- .red {
- position: fixed;
- width: 200px;
- height: 200px;
- background-color: red;
- border: 10px solid black;
- }
-
- .blue {
- position: relative;
- left: -10px;
- top: -10px;
- z-index: -1;
-
- width: 100px;
- height: 100px;
- background-color: blue;
- }
- style>
- head>
- <body>
- <div class="red">
- <div class="blue">div>
- div>
- body>
- html>

- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Documenttitle>
- <style>
- body { background-color: blue; }
-
- .red-sc {
- position: absolute;
- left: 50px;
- z-index: -1;
-
- width: 100px;
- height: 100px;
- background-color: red;
- }
-
- .yellow-block {
- width: 100px; height: 100px; background-color: yellow;
- }
-
- .skyblue-float {
- width: 100px; height: 100px; background-color: skyblue; float: left; margin-left: 50px; margin-top: 50px;
- }
-
-
- style>
- head>
- <body>
- <div class="red-sc">div>
- <div class="skyblue-float">div>
- <div class="yellow-block">div>
- <span style="color: white">我是行内元素span>
- <button>我是行内块元素button>
- body>
- html>

- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Documenttitle>
- <style>
- div { width: 100px; height: 100px; }
-
- .skyblue-float {
- background-color: skyblue; float: left;
- }
-
- .red-z-auto {
- background-color: red; position: relative; z-index: auto; left: 50px; top: 50px;
- }
-
- .blue-z-0 {
- background-color: blue; position: relative; z-index: 0;
- }
-
- .green-z-none {
- background-color: green; position: fixed; left: 50px; top: 130px;
- }
- style>
- head>
- <body>
- <div class="skyblue-float">div>
- <div class="red-z-auto">div>
- <div class="blue-z-0">div>
- <div class="green-z-none">div>
- body>
- html>

- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Documenttitle>
- <style>
- div { width: 100px; height: 100px; }
-
- .skyblue-float {
- background-color: skyblue; float: left;
- }
-
- .red-z-auto {
- background-color: red; position: relative; z-index: auto;
- }
-
- .blue-z-0 {
- background-color: blue; position: relative; z-index: 0; left: 50px; top: 50px;
- }
-
- .green-z-none {
- background-color: green; position: fixed; left: 50px; top: 100px;
- }
- style>
- head>
- <body>
- <div class="skyblue-float">div>
- <div class="green-z-none">div>
- <div class="blue-z-0">div>
- <div class="red-z-auto">div>
- body>
- html>

- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Documenttitle>
- <style>
- div { width: 100px; height: 100px; }
-
- .skyblue-float {
- background-color: skyblue; float: left;
- }
-
- .red-z-auto {
- background-color: red; position: relative; z-index: auto; left: 50px; top: 50px;
- }
-
- .blue-z-0 {
- background-color: blue; position: relative; z-index: 0;
- }
-
- .green-z-none {
- background-color: green; position: fixed; left: 50px; top: 130px;
- }
-
- .black-z-positive {
- background-color: black; position: absolute; z-index: 1; top: 80px; left: 30px;
- }
- style>
- head>
- <body>
- <div class="skyblue-float">div>
- <div class="red-z-auto">div>
- <div class="blue-z-0">div>
- <div class="green-z-none">div>
- <div class="black-z-positive">div>
- body>
- html>

在学习绝对定位之前,我们需要先了解包含块概念
布局和包含块 - CSS(层叠样式表) | MDN (mozilla.org)https://developer.mozilla.org/zh-CN/docs/Web/CSS/Containing_block我们通过一个例子来了解包含块是啥

上例中,blue元素的大小、位置都是使用的百分比,而不是固定数值,但是blue元素的最终大小、位置如下:
width:150px、height:100px
left:150px、top:100px
我们可以很快分析出,blue元素的位置、大小数据是基于其父元素red的内容区大小width:300px、height:200px乘以百分比计算得到的。
此时blue元素的包含块就是其父元素的内容区。
也就是说:元素的位置和大小受其包含块影响。
那么元素的包含块总是该元素的父元素的内容区吗?
不是的。
我们看下面例子

上例中,blue元素已经变为了绝对定位元素,其父元素red还是普通块级元素,其祖先元素green是相对定位元素。此时:blue元素的大小、位置是基于green元素的padding区的(green元素padding区的大小为宽度200+20+20=440px,高度300+20+20=340px)计算出来的。
下面我们给定元素的包含块的完整定义:
首先,确定一个元素的包含块的过程,完全依赖于这个元素的position属性。
什么是初始化包含块?
根元素 () 所在的包含块是一个被称为初始包含块的矩形。他的尺寸是连续媒体的视口 viewport (for continuous media) 或分页媒体 page media (for paged media).
什么是连续媒体?
Continuous Media - 术语表 | MDN (mozilla.org)
什么是分页媒体?
Paged media - CSS(层叠样式表) | MDN (mozilla.org)
简单总结就是:
连续媒体可以通过滚动条展示连续的,超出屏幕尺寸大小的内容(如网页)
分页媒体的内容不能超出屏幕尺寸,用户通过切页来查看内容(如PPT)
浏览器中一般都是连续媒体,所以我们只考虑连续媒体。
什么是viewport?
视口概念 - CSS(层叠样式表) | MDN (mozilla.org)
viewport即视口,在浏览器中视口,就是展示网页的区域,如下红框区域
视口有两种:视觉视口、布局视口
当网页很大时,或者浏览器窗口缩放时,浏览器视口只能展示部分网页内容,而网页看不见的内容需要通过浏览器滚动条来滚动查看。
看的见网页的区域就是视觉视口,整个网页就是布局视口。
根据包含块定义:
那么上述两种情况的视口是同一种视口吗?


我们将body设为宽高3000px,保证其布局视口远大于视觉视口,
然后,设定红色盒子为绝对定位且祖先无定位元素,蓝色盒子为固定定位。
当我们滚动浏览器滚动条时,发现视觉视口中:蓝色盒子位置不变,而红色盒子的位置改变。
所以很容易地得出结论:
当我们为一个元素添加position:absolute样式后,该元素就变为了绝对定位元素。绝对定位元素具有以下特点:
而绝对定位元素的包含块是:
下面对绝对定位元素以上特性进行验证。
下面例子中红色div一开始没有绝对定位,当为其添加绝对定位后,红色div脱标,并释放了标准流中的位置,而它标准流之后的元素蓝色div立即就进据了被释放的位置。

所以绝对定位元素会脱标。
absolute元素的边偏移都是相对于它的包含块进行的。
如果absolute元素的祖先存在定位(非static)元素,则最近一级定位祖先元素的内边距区就是它的包含块。
- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Documenttitle>
- <style>
- html, body {
- margin: 0;
- padding: 0;
- }
-
- .red {
- position: relative;
-
- width: 200px;
- height: 200px;
-
- padding: 50px;
- border: 10px solid black;
- margin: 10px;
-
- background-color: red;
- }
-
- .blue {
- position: absolute;
- left: 0;
- top: 0;
-
- width: 50px;
- height: 50px;
- background-color: blue;
- }
- style>
- head>
- <body>
- <div class="red">
- red内容区
- <div class="blue">div>
- div>
- body>
- html>
上例中,red元素存在一个padding:50px的内边距的定位元素,而blue元素是绝对定位的,且大小为50px,偏移为0,此时我们发现blue元素是以red的padding边缘为参照物进行偏移的,而不是red的content区域。
如果absolute元素的祖先不存在定位元素,则浏览器的布局视口就是它的包含块
- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Documenttitle>
- <style>
- body {
- margin: 0;
- padding: 50px;
- }
-
- .red {
- width: 200px;
- height: 200px;
- background-color: red;
- }
-
- .blue {
- position: absolute;
- left: 0;
- top: 0;
-
- width: 50px;
- height: 50px;
- background-color: blue;
- }
- style>
- head>
- <body>
- <div class="red">
- <div class="blue">div>
- div>
- body>
- html>

如上例中,red是普通块级元素,而blue是绝对定位元素,所以blue元素的祖先没有定位元素。blue元素的包含块就是浏览器布局视口 ,而不是body元素的padding区。
什么是子绝父相?
即父级元素采用相对定位,子级元素采用绝对定位。
为什么要子绝父相?
父级元素相对定位,实现自己控制自己的位置
子级元素绝对定位,将参照父相元素进行偏移,即:子绝元素的位置受到父相元素的控制。
如下面这个对话框,
对话框本身是相对定位,这样可以实现自己控制自己的显示位置,不受其他元素影响,
对话框的关闭×按钮是绝对定位,它总是处于对话框的右上角位置,其位置可以参照父相元素的位置进行计算。

子绝父标行不行?
即父元素在标准流中,子元素采用绝对定位。缺点有两个:
子绝父绝行不行?
即父元素是绝对定位,子元素也是绝对对位。缺点有两个
子相父绝行不行?
父子各玩各的,肯定不行。
方案一:
- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Documenttitle>
- <style>
- .red {
- position: relative;
- width: 200px;
- height: 200px;
- background-color: red;
- }
-
- .blue {
- position: absolute;
- left: 50%;
- top: 50%;
- margin-left: -25px;
- margin-top: -25px;
-
- width: 50px;
- height: 50px;
- background-color: blue;
- }
- style>
- head>
- <body>
- <div class="red">
- <div class="blue">div>
- div>
- body>
- html>

后面学了2D位移,还可以只要搞。

上面这类居中方案思路比较简单,就是先让绝对定位元素的父元素变为相对定位(PS:父元素变为相对定位是不会改变页面布局的,所以可以放心变) ,此时父元素的内容区就是绝对定位元素的包含块,绝对定位元素left:50%,top:50%,相当于left:包含块宽度*50%,right:包含块高度*50%,表现为绝对定位元素的左上角点 移动到 父元素中心点,此时绝对定位元素还没有居中,我们继续让绝对定位元素左移自身宽度一半,上移自身高度一般,才能达到全居中。
方案二:
- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Documenttitle>
- <style>
- .red {
- position: relative;
- width: 200px;
- height: 200px;
- background-color: red;
- }
-
- .blue {
- position: absolute;
- left: 0;
- top: 0;
- right: 0;
- bottom: 0;
- margin: auto;
-
-
- width: 50px;
- height: 50px;
- background-color: blue;
- }
- style>
- head>
- <body>
- <div class="red">
- <div class="blue">div>
- div>
- body>
- html>

这种实现方案还适用于绝对定位元素在body中居中

以上方案的关键点有两个:
首先margin:auto,相当于 margin: auto auto
我们之前学习过margin: 0 auto让标准流中块级元素水平居中,原理是:
标准流中块级元素独占一行,如果块级元素盒子总宽度不足以占据一行,则块级元素的右margin会自动填满剩余宽度,而margin: 0 auto的意识是让块级元素的左右margin平分所在行的剩余宽度。这样就能是标准流中块级元素水平居中了。
margin: auto auto 的意思是让:元素的左右margin平分水平方向的剩余宽度,元素的上下margin平分垂直方向的剩余宽度。
好了,接下来解释:绝对定位元素四个偏移全部为0的作用
left:0,表示让绝对定位元素的左外边距 距离 其包含块的左边界为0
right:0,表示让绝对定位元素的右外边距 距离 其包含块的右边界为0
......
那么对于设置宽高的绝对定位元素,这似乎是不可能的。如下图。

那么如果绝对定位元素没有设置宽高呢?

我们发现未设置宽高的绝对定位元素自动占满了包含块。
确定是自动占满的是包含块吗?我们再给一个例子证明下:
上例中blue元素是未设宽高的绝对定位盒子,其包含块是最近一级的相对定位父元素red,所以blue元素的包含块就是red元素的padding以内区域。而最终blue也是自适应了其包含块大小。
而对于设置了宽高的绝对定位元素,让其四个偏移为0,其实它也会尝试自动占满包含块,但是由于自身设置了宽高,所以无法占满,此时绝对定位元素只能通过margin来占满包含块内剩余宽高。

此时我们再设置绝对定位元素的margin:auto,则表示让绝对定位元素的左右margin平分其包含块内水平剩余宽度,让上下margin平分其包含块内垂直剩余高度 。
当我们给标准流中元素添加position:fixed样式后,该元素就会变成固定定位元素,特点是:
固定定位元素不会随着页面滚动条滚动,而是保持在视觉视口中的固定位置。
固定定位元素的位置也不受其他元素的影响,因为固定定位元素的位置只受其包含块影响,而它的包含块是浏览器视觉视口。
- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Documenttitle>
- <style>
- html, body {
- margin: 0;
- padding: 0;
- }
-
- body {
- height: 3000px;
- width: 3000px;
- }
-
- .blue {
- position: fixed;
- left: 0;
- top: 0;
-
- width: 50px;
- height: 50px;
- background-color: blue;
- }
-
- .red {
- position: relative;
- left: 0;
- top: 0;
-
- width: 200px;
- height: 200px;
- background-color: red;
- }
- style>
- head>
- <body>
- <div class="red">
- <div class="blue">div>
- div>
- body>
- html>

大部分网站网页在版心右侧都会设置一些功能键,如B站的

这些功能键要求
那么如何让固定定位的元素位置放在紧贴版心右侧的位置呢?
- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Documenttitle>
- <style>
- html, body {
- width: 100%;
- margin: 0;
- padding: 0;
- }
-
- .w {
- width: 80%;
- margin: 0 auto;
- }
-
- .container {
- height: 3000px;
- background-color: skyblue;
- }
-
- .w-right-fix-area {
- position: fixed;
- left: 50%;
- top: 70%;
- margin-left: 41%;
- }
-
- .button {
- width: 40px;
- padding: 5px 10px;
- font-size: 20px;
- border: 1px solid #e3e5e7;
- border-radius: 5px;
- margin-top: 10px;
- cursor: pointer;
- user-select: none;
- }
-
- .button:hover {
- background-color: #e3e5e7;
- }
- style>
- head>
- <body>
- <div class="w container">div>
- <div class="w-right-fix-area">
- <div class="button">新版功能div>
- <div class="button">回到顶部div>
- div>
- body>
- html>

上例中,实现元素固定在版心右侧的关键是:样式类w-right-fix-area
w-right-fix-area元素是固定定位的,所以它的包含块总是浏览器视觉视口,
此时w-right-fix-area元素位置如下

然后再让w-right-fix-area元素margin-left版心宽度一半的距离即可。
版心w元素的width是其包含块内容区宽度80%,而w元素的包含块就是其最近的祖先块级容器的内容区,即body的内容区,而body的内容区宽度是100%,即浏览器视觉视口宽度的100%,因此w元素一半的距离就是浏览器视口宽度的40%

由于margin-left:40% 会让w-right-fix-area元素紧贴版心右侧,不是很好看,所以margin-left:41%

在了解粘性定位之前,我们先来看看粘性效果是啥样的

工商银行官网首页的导航栏就具有粘性效果,而工商银行的导航栏确实基于JS实现的。
下面我们通过JS来复刻下工商银行网站导航栏的效果
- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Documenttitle>
- <style>
- html, body, h1 {
- width: 100%;
- margin: 0;
- padding: 0;
- }
-
- body {
- min-width: 1600px;
- }
-
- /* 头部 */
- .head {
- height: 143px;
- }
-
- /* 导航栏,实现粘性定位 */
- .nav {
- width: 100%;
- height: 54px;
- background-color: #c7000b;
- left: 0;
- top: 0;
- }
-
-
- /* 导航栏内容排版,和粘性定位无关 */
- ul {
- display: flex;
- justify-content: center;
- margin: 0;
- padding: 0;
- list-style: none;
- }
-
- ul li:hover {
- background-color: #af0812;
- }
-
- ul li h1 {
- font-size: 24px;
- font-weight: normal;
- height: 54px;
- }
-
- ul li a {
- line-height: 54px;
- text-decoration: none;
- color: white;
- padding: 0 40px;
- border-right: 1px solid #ef1f2a;
- border-left: 1px solid #86232a;
- }
-
- ul li:first-child a {
- padding: 0;
- border-left: none;
- }
-
- ul li:last-child a {
- padding: 0;
- border-right: none;
- }
-
- /* 主体内容 */
- .content {
- height: 2000px;
- }
- style>
- head>
- <body>
- <div class="head">div>
- <div class="nav">
- <ul>
- <li><a href="#">a>li>
- <li><h1><a href="#">账户服务a>h1>li>
- <li><h1><a href="#">存款和贷款a>li>
- <li><h1><a href="#">信用卡a>li>
- <li><h1><a href="#">外汇业务a>li>
- <li><h1><a href="#">投资理财a>li>
- <li><h1><a href="#">私人银行a>li>
- <li><h1><a href="#">金融市场a>li>
- <li><a href="#">a>li>
- ul>
- div>
- <div class="content">div>
- <script>
- const nav = document.querySelector('.nav')
- const shreshold = nav.offsetTop
-
- document.onscroll = function(){
- if(window.pageYOffset >= shreshold) {
- nav.style.position = 'fixed'
- } else {
- nav.style.position = 'static'
- }
- }
- script>
- body>
- html>

可以发现,基于JS实现粘性很简单,其实就是将
但是JS实现粘性定位有一个问题,那就是当粘性元素变为固定定位后,会脱标,导致它之后的页面布局改变,比如工商银行的网站导航栏下方元素就有明显的塌陷抖动

从动图中可以发现,当nav导航栏到达顶部后,“个人网上银行登录”这个盒子明显地塌陷抖动了一下。
下面我们使用CSS3的粘性定位来实现上面的导航栏
- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Documenttitle>
- <style>
- html, body, h1 {
- width: 100%;
- margin: 0;
- padding: 0;
- }
-
- body {
- min-width: 1600px;
- }
-
- /* 头部 */
- .head {
- height: 143px;
- }
-
- /* 导航栏,实现粘性定位 */
- .nav {
- position: sticky;
- top: 0;
- width: 100%;
- height: 54px;
- background-color: #c7000b;
- }
-
-
- /* 导航栏内容排版,和粘性定位无关 */
- ul {
- display: flex;
- justify-content: center;
- margin: 0;
- padding: 0;
- list-style: none;
- }
-
- ul li:hover {
- background-color: #af0812;
- }
-
- ul li h1 {
- font-size: 24px;
- font-weight: normal;
- height: 54px;
- }
-
- ul li a {
- line-height: 54px;
- text-decoration: none;
- color: white;
- padding: 0 40px;
- border-right: 1px solid #ef1f2a;
- border-left: 1px solid #86232a;
- }
-
- ul li:first-child a {
- padding: 0;
- border-left: none;
- }
-
- ul li:last-child a {
- padding: 0;
- border-right: none;
- }
-
- /* 主体内容 */
- .content {
- height: 2000px;
- }
- style>
- head>
- <body>
- <div class="head">div>
- <div class="nav">
- <ul>
- <li><a href="#">a>li>
- <li><h1><a href="#">账户服务a>h1>li>
- <li><h1><a href="#">存款和贷款a>li>
- <li><h1><a href="#">信用卡a>li>
- <li><h1><a href="#">外汇业务a>li>
- <li><h1><a href="#">投资理财a>li>
- <li><h1><a href="#">私人银行a>li>
- <li><h1><a href="#">金融市场a>li>
- <li><a href="#">a>li>
- ul>
- div>
- <div class="content">div>
- body>
- html>
其实改动很小,实现的效果一样,并且不会发生塌陷抖动。
改动就是,去除JS代码,然后给.nav添加了样式
下面是MDN给出粘性定位的定义,我们来分析下:
元素根据正常文档流进行定位,然后相对它的最近滚动祖先(nearest scrolling ancestor)和 containing block (最近块级祖先 nearest block-level ancestor),包括 table-related 元素,基于
top,right,bottom, 和left的值进行偏移。偏移值不会影响任何其他元素的位置。
该值总是创建一个新的层叠上下文(stacking context)。注意,一个 sticky 元素会“固定”在离它最近的一个拥有“滚动机制”的祖先上(当该祖先的
overflow是hidden,scroll,auto, 或overlay时),即便这个祖先不是最近的真实可滚动祖先。这有效地抑制了任何“sticky”行为(详情见Github issue on W3C CSSWG)。
这句话的意思其实就是:粘性定位的元素不会脱标,不会影响标准流布局
top, right, bottom, 和 left的值进行偏移。其实这段话应该是有歧义的。
粘性定位的边偏移其实是相对于其最近的有滚动机制的祖先元素而言的,
- 当粘性定位元素距离其滚动机制祖先元素内容区未被隐藏的区域的边界小于设定的偏移值时,粘性定位元素位置将固定在设定的偏移值位置;(表现为固定定位)
- 当粘性定位元素距离其滚动机制祖先元素内容区未被隐藏的区域的边界大于设定的偏移值时,粘性定位元素会随着包含块移动;(表现为静态定位)
这个有点类似于相对定位,相对定位的元素本体不脱标,进行边偏移的是本体的影子,所以相对定位元素的偏移位置改变不会其他元素。
粘性定位应该也有本体和影子的概念,本体保持对标准流位置的占据,影子进行边偏移。
意思是粘性定位不需要依赖z-index就可以生成一个层叠上下文,且未定义z-index的粘性定位元素在z轴显示优先级只低于z-index为正数的定位元素。
overflow 是 hidden, scroll, auto, 或 overlay时),即便这个祖先不是最近的真实可滚动祖先。这段话意思是,粘性元素会选择一个最近的具有overflow非visible的祖先作为固定定位的相对元素,即使这个祖先并没有滚动条,比如overflow:hidden的元素就没有滚动条。
总结一下粘性定位的特点:
下面我们对上述特点进行验证:
- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Documenttitle>
- <style>
- body {
- height: 3000px;
- }
-
- .blue {
- width: 500px;
- height: 500px;
- padding: 100px;
- border: 50px solid black;
- margin: 50px;
- color: white;
- background-color: blue;
- overflow: auto;
- }
-
- .red {
- position: sticky;
- top: 200px;
-
- width: 100%;
- height: 100px;
- background-color: red;
- }
-
- .green {
- width: 100%;
- height: 400px;
- background-color: green;
- }
- style>
- head>
- <body>
- <div class="blue">
- blue内容区
- <div class="green">div>
- <div class="red">div>
- <div class="green">div>
- div>
- body>
- html>

上面动态可以验证:

上面动图可以验证:粘性定位元素red只会固定在最近的具有滚动机制的父元素上,而不会固定在其他具有滚动机制的元素上,如上面的body元素。

并且当我们将blue的overflow改为hidden,即blue只是具有滚动机制的元素,但是没有滚动条,red依旧把blue当成最近的具有滚动机制的祖先元素。
浮动 + 相对定位(√)

上例中,red元素既是浮动元素,也是相对定位元素,并且red同时具备了浮动特性和相对定位特性。另外red此时会脱标。
浮动 + 绝对定位(×)

可以发现,如果一个元素同时有浮动和绝对定位,则只有绝对定位生效。
浮动 + 固定定位(×)

可以发现,如果一个元素同时有固定定位和浮动,则只有固定定位生效
浮动 + 粘性定位(√)

可以发现,一个元素同时有粘性定位和浮动,则多生效。且此时元素脱标。
总结:对于会导致元素脱标的定位,如:绝对定位、固定定位,则元素浮动被抑制;对于不会导致元素脱标的定位,如:相对定位、粘性定位,则元素浮动也生效。