场景
在项目中,常常会碰上这么个效果,就是当页面滚动时要使某个元素(如导航栏、侧边栏、表头之类的)滚动到指定的位置不再滚动。
要想实现这个效果,就得用到接下来所讲的粘性定位。
什么是粘性定位
position: sticky
是 CSS 中一种特殊的定位方式,它可以让元素在滚动到特定位置时"粘住"在视口中。
基本用法:
.sticky-element {
position: sticky;
top: 0; /* 触发粘性的阈值 */
}
工作原理:
- 常规定位阶段:元素最初表现为相对定位(relative),保留在文档流中
- 粘性阶段:当视口滚动到元素达到指定的阈值(如
top: 0
)时,元素"粘住"在视口中 - 脱离阶段:当元素的父容器完全滚出视口时,元素会随父容器一起滚出。即粘性元素只能在父容器内粘住,当父容器滚出视口时,粘性元素会随之滚出。
案例
案例1
<!DOCTYPE html>
<html>
<head>
<style>
body {
margin: 0;
font-family: Arial;
/* 增加页面高度以演示滚动 */
height: 2000px;
}
/* 粘性导航栏 */
.sticky-nav {
position: sticky;
top: 0; /* 触发阈值:当导航栏碰到视口顶部时固定 */
background: #333;
color: white;
padding: 15px;
text-align: center;
}
/* 内容区块 */
.content {
padding: 20px;
height: 800px; /* 增加高度以便滚动 */
background: #f0f0f0;
}
</style>
</head>
<body>
<div class="content">
<p>滚动页面向下 ↓↓↓</p>
</div>
<!-- 粘性导航栏 -->
<div class="sticky-nav">我是粘性导航栏</div>
<div class="content">
<p>继续滚动观察导航栏行为</p>
</div>
</body>
</html>
-
父元素是body元素,同时增加了body的高度是2000px,使其能够滚动。
-
给导航栏设置
position: sticky; top: 0
,当导航栏碰到视口顶部时固定。
这里需要注意top:0;
是相对于离该元素最近的具有滚动条的祖先元素,这里的具有滚动条的元素是body。
案例二
这个案例在上面案例的基础上增加了一个退出的功能。
当粘性元素的父元素离开了视口的时候,那么粘性元素也会随之退出。
<!DOCTYPE html>
<html>
<head>
<style>
body {
margin: 0;
font-family: Arial;
}
/* 父容器 - 限制粘性元素的作用范围 */
.container {
height: 800px; /* 明确高度 */
background: #f0f0f0;
margin-bottom: 50px; /* 增加间隔 */
border: 2px dashed #999; /* 可视化父容器边界 */
}
/* 粘性元素 */
.sticky-box {
position: sticky;
top: 20px; /* 触发阈值 */
height: 60px;
background: #ff6b6b;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
}
/* 普通内容块 */
.content {
height: 200px;
padding: 20px;
}
</style>
</head>
<body>
<div class="content">
<h2>向下滚动 ↓↓↓</h2>
<p>观察红色粘性块的行为变化</p>
</div>
<!-- 粘性元素的父容器 -->
<div class="container">
<div class="content">
<p>父容器内的内容(上方空间)</p>
</div>
<!-- 粘性元素 -->
<div class="sticky-box">我是粘性元素 (top: 20px)</div>
<div class="content">
<p>继续滚动我会先固定,然后突然消失(脱离阶段)</p>
</div>
</div>
<div class="content">
<h2>父容器外部的区域</h2>
<p>当父容器完全滚出视口时,粘性元素会解除固定状态</p>
</div>
<div class="content">
<h2>父容器外部的区域</h2>
<p>当父容器完全滚出视口时,粘性元素会解除固定状态</p>
</div>
<div class="content">
<h2>父容器外部的区域</h2>
<p>当父容器完全滚出视口时,粘性元素会解除固定状态</p>
</div>
<div class="content">
<h2>父容器外部的区域</h2>
<p>当父容器完全滚出视口时,粘性元素会解除固定状态</p>
</div>
</body>
</html>
通过上面两个案例,我们理解了粘性元素的使用,这里总结一下:
-
确定粘性元素,给其添加
position: sticky
属性; -
确定触发粘性行为的位置,即你希望它滚动到哪个位置不再滚动,如
top:0;
(注意:这里的top:0;
是相对于离该元素最近的具有滚动条的祖先元素,一般是视窗);
<div style="height: 300px; overflow: auto; border: 1px solid black">
<!-- 滚动容器 -->
<div style="height: 1000px">
<div style="height: 80px; background-color: aquamarine"></div>
<div
style="position: sticky; top: 20px; background-color: antiquewhite"
>
我是粘性元素
</div>
<div style="height: 80px; background-color: aquamarine"></div>
</div>
</div>
-
何时失效:当粘性元素的边缘和父元素的临界元素边缘重合时,会随着父元素一起滚走,这时粘性就失效了。也就是父元素离开了视口。
-
Z-index问题:在某些情况下,如果页面上其他元素的
z-index
较高,可能会覆盖或影响粘性元素的可见性。
实现通讯录的功能
当页面滚动时,效果图如下:
<div
class="relative max-w-md mx-auto bg-white dark:bg-slate-800 shadow-lg h-80 overflow-auto ring-1 ring-slate-900/5 -my-px"
>
<div class="relative">
<div
class="sticky top-0 px-4 py-3 flex items-center font-semibold text-sm text-slate-900 dark:text-slate-200 bg-slate-50/90 dark:bg-slate-700/90 backdrop-blur-sm ring-1 ring-slate-900/10 dark:ring-black/10"
>
A
</div>
<div class="divide-y dark:divide-slate-200/5">
<div class="flex items-center gap-4 p-4">
<img
class="w-12 h-12 rounded-full"
src="https://images.unsplash.com/photo-1501196354995-cbb51c65aaea?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=facearea&facepad=4&w=256&h=256&q=80"
/>
<strong class="text-slate-900 text-sm font-medium dark:text-slate-200"
>Andrew Alfred</strong
>
</div>
<div class="flex items-center gap-4 p-4">
<img
class="w-12 h-12 rounded-full"
src="https://images.unsplash.com/photo-1531123897727-8f129e1688ce?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=facearea&facepad=4&w=256&h=256&q=80"
/>
<strong class="text-slate-900 text-sm font-medium dark:text-slate-200"
>Aisha Houston</strong
>
</div>
<div class="flex items-center gap-4 p-4">
<img
class="w-12 h-12 rounded-full"
src="https://images.unsplash.com/photo-1517841905240-472988babdf9?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=facearea&facepad=4&w=256&h=256&q=80"
/>
<strong class="text-slate-900 text-sm font-medium dark:text-slate-200"
>Anna White</strong
>
</div>
<div class="flex items-center gap-4 p-4">
<img
class="w-12 h-12 rounded-full"
src="https://images.unsplash.com/photo-1531427186611-ecfd6d936c79?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=facearea&facepad=4&w=256&h=256&q=80"
/>
<strong class="text-slate-900 text-sm font-medium dark:text-slate-200"
>Andy Flint</strong
>
</div>
</div>
</div>
<div class="relative">
<div
class="sticky top-0 px-4 py-3 flex items-center font-semibold text-sm text-slate-900 dark:text-slate-200 bg-slate-50/90 dark:bg-slate-700/90 backdrop-blur-sm ring-1 ring-slate-900/10 dark:ring-black/10"
>
B
</div>
<div class="divide-y dark:divide-slate-200/5">
<div class="flex items-center gap-4 p-4">
<img
class="w-12 h-12 rounded-full"
src="https://images.unsplash.com/photo-1501196354995-cbb51c65aaea?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=facearea&facepad=4&w=256&h=256&q=80"
/>
<strong class="text-slate-900 text-sm font-medium dark:text-slate-200"
>Bob Alfred</strong
>
</div>
<div class="flex items-center gap-4 p-4">
<img
class="w-12 h-12 rounded-full"
src="https://images.unsplash.com/photo-1531123897727-8f129e1688ce?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=facearea&facepad=4&w=256&h=256&q=80"
/>
<strong class="text-slate-900 text-sm font-medium dark:text-slate-200"
>Bianca Houston</strong
>
</div>
<div class="flex items-center gap-4 p-4">
<img
class="w-12 h-12 rounded-full"
src="https://images.unsplash.com/photo-1517841905240-472988babdf9?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=facearea&facepad=4&w=256&h=256&q=80"
/>
<strong class="text-slate-900 text-sm font-medium dark:text-slate-200"
>Brianna White</strong
>
</div>
<div class="flex items-center gap-4 p-4">
<img
class="w-12 h-12 rounded-full"
src="https://images.unsplash.com/photo-1531427186611-ecfd6d936c79?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=facearea&facepad=4&w=256&h=256&q=80"
/>
<strong class="text-slate-900 text-sm font-medium dark:text-slate-200"
>Bert Flint</strong
>
</div>
</div>
</div>
<div class="relative">
<div
class="sticky top-0 px-4 py-3 flex items-center font-semibold text-sm text-slate-900 dark:text-slate-200 bg-slate-50/90 dark:bg-slate-700/90 backdrop-blur-sm ring-1 ring-slate-900/10 dark:ring-black/10"
>
C
</div>
<div class="divide-y dark:divide-slate-200/5">
<div class="flex items-center gap-4 p-4">
<img
class="w-12 h-12 rounded-full"
src="https://images.unsplash.com/photo-1501196354995-cbb51c65aaea?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=facearea&facepad=4&w=256&h=256&q=80"
/>
<strong class="text-slate-900 text-sm font-medium dark:text-slate-200"
>Colton Alfred</strong
>
</div>
<div class="flex items-center gap-4 p-4">
<img
class="w-12 h-12 rounded-full"
src="https://images.unsplash.com/photo-1531123897727-8f129e1688ce?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=facearea&facepad=4&w=256&h=256&q=80"
/>
<strong class="text-slate-900 text-sm font-medium dark:text-slate-200"
>Cynthia Houston</strong
>
</div>
<div class="flex items-center gap-4 p-4">
<img
class="w-12 h-12 rounded-full"
src="https://images.unsplash.com/photo-1517841905240-472988babdf9?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=facearea&facepad=4&w=256&h=256&q=80"
/>
<strong class="text-slate-900 text-sm font-medium dark:text-slate-200"
>Cheyenne White</strong
>
</div>
<div class="flex items-center gap-4 p-4">
<img
class="w-12 h-12 rounded-full"
src="https://images.unsplash.com/photo-1531427186611-ecfd6d936c79?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=facearea&facepad=4&w=256&h=256&q=80"
/>
<strong class="text-slate-900 text-sm font-medium dark:text-slate-200"
>Charlie Flint</strong
>
</div>
</div>
</div>
</div>
上面代码的CSS采用tailwindcss
。
-
通过设置
sticky top-0
, A、B等元素变成了粘性元素。 -
当滚动时,因为给A设置了
top:0;
,所以A相对于最近的具有滚动条的祖先元素保持不动。 -
当继续滚动时,粘性元素的边缘和父元素的临界元素边缘重合时,会随着父元素一起滚走。也就是父元素都滚出了视口。
-
此时,B粘性元素就顶上去,当距离祖先滚动元素的距离等于
top:0;
时,就保证固定不动。
这样就用很简单的代码实现了一个通讯录的功能。