前言
我们知道,position: sticky
可以很轻松帮助我们实现粘性定位,但目前存在一个很大的问题:我们无法检测粘性元素是否已经粘在某一侧。
这是一个很常见的需求,我们很可能需要在元素粘住的时候添加相应样式(例如导航栏粘在顶部后添加阴影)如果要使用 JS 去检测滚动偏移去计算,那将消耗比较多的资源,且需要考虑的情况也很多。虽然 CSS 也有比较 hack 的方法实现这样的阴影,但更多其他需求很难说都能做到。
与此类似的,其他与滚动相关的状态我们也很难检测,例如:容器是否可滚动、是否可以朝某个方向滚动、滚动是否已经吸附等等,这些东西在新的CSS容器查询中都将得到解决
容器查询
什么是容器查询?简单来说就是查询某个元素是否匹配某些特定的条件,并给其下面的元素设置指定样式。这类似于@media
媒体查询,不同的是容器查询使用@container
,且我们必须在目标元素通过CSS container-type
指定需要查询的条件,之前支持的条件有:
inline-size
: 可查询容器的内联尺寸(默认为宽度)size
: 可查询容器的宽度和高度normal
: 可查询容器的某些样式是否满足特定的值
.post {
container-type: inline-size;
}
@container (width > 650px) and (width < 1000px) {
/* 如果.post的宽度满足范围,其下面的.card将会生效该样式 */
.card {
width: 50%;
background-color: gray;
font-size: 1em;
}
}
.card {
color: white;
/** 也可以将@container查询嵌套在一般的声明中,它与上面的声明效果相同。这需要浏览器支持嵌套@声明 */
@container (width > 650px) {
color: red;
}
}
@container style(color: red) or style(border: 2px solid red) {
/** 与@media类似,条件可以通过 and or not 来组合。当样式满足条件,则该查询生效 */
}
早在 2023 年初各大浏览器就均已支持上述的容器查询,更多介绍和用法可参考MDN或其他文章,本次我们带来的是容器查询最新支持的滚动状态查询。在最新的 Edge 或 Chrome 133版本中,container-type
新增了scroll-state
支持,拥有该值的元素支持以下三种滚动状态查询。
查询粘性元素是否粘住
粘性元素在到达指定偏移之前会表现为静态定位,滚动指定偏移后才会粘在某一侧。在之前,无论是 JS 还是 CSS 都没有十分有效的方法查询粘性元素是否粘住。依靠 JS 监听滚动有一个非常大的问题,我们无法监听offsetTop
这类值的变化,只能退而求其次监听其他元素的 Resize 和 Mutation,亦或是不断轮询,这是非常麻烦的。
而容器查询可通过scroll-state(stuck: top)
直接查询粘性元素是否在某一方向粘住(stuck),方向的值可以为left
、right
、top
、bottom
以及其对应的四个逻辑方向,另外none
也是有效值,用于查询未粘住的情况。具体使用如下:
div {
container-type: scroll-state;
position: sticky;
top: 20px;
nav {
@container scroll-state(stuck: top) {
background: var(--surface-1);
box-shadow: var(--shadow-5);
border-radius: 10px;
margin-inline: 20px;
}
}
}
完整的高级示例可在线打开CodePen参考,注意需版本在133之上的 Edge 或 Chrome
查询指定方向是否可滚动
一些情况我们可能需要查询容器是否可以往某个方向滚动,并使某些元素动态展示出来。最常见的当属“回到顶部”按钮,又或者是横向滚动发生后展示左右箭头按钮便于用户点击,亦或是展示阴影来表明某个方向可以滚动。这些通过 JS 倒是不难实现,但现在可通过 CSS 更加高效地实现。
通过scroll-state(scrollable: top)
可查询其是否可以往某个方向滚动。可指定的值与上例相同,另外none
代表无可滚动方向,即不可滚动
.cotainer {
container-type: scroll-state;
.to-top {
opacity: 0;
pointer-events: none;
@container scroll-state(scrollable: top) {
/** 当容器可以向上滚动时,将“回到顶部”按钮展示出来 */
opacity: 1;
pointer-events: all;
}
}
}
完整的高级示例可参考CodePen
查询目标是否即将滚动吸附或已吸附
我们知道scroll-snap-type
可以为滚动容器设置滚动吸附,当滚动一定距离后自动滚动到指定位置,使目标吸附到滚动容器,即轮播图的效果。
如果要做一些比较生动的效果,例如某一目标即将吸附时,该目标进行高亮。在 JS 层面我们之前可以通过scrollsnapchanging
和scrollsnapchange
事件获得比较准确的时机,做起来稍显麻烦,但若是通过监听滚动自行计算那就更麻烦了。
新的容器查询scroll-state(snapped: x)
可查询目标是否即将或者已经吸附(snap)到滚动容器,其可以指定x
,y
以及对应的两个逻辑方向block
和inline
,也可以指定both
查询双边,另外none
可以用于查询其是否为滚动吸附目标
比较高级的示例可在线打开CodePen1和CodePen2参考
总结
CSS 容器查询新增了scroll-state
支持,而scroll-state
可以查询stuck
(粘性元素)、scrollable
(可滚动)、snapped
(滚动吸附)三种滚动相关状态。目前仅 Chromium 系浏览器 133+ 的版本支持,只适合了解或对兼容性没有要求的项目使用。
未来的标准将在 DOM 元素上添加matchContainer()
方法,这类似于window.matchMedia()
方法,可用于在 JS 动态监听某些条件是否生效,灵活性又大大提升,不过短期是看不到了。
在133以及未来的134版本中,谷歌还新增了不少比较重量级的特性,未来几天会抽空输出,感兴趣的可以关注一波哦,以后也会不断给大家带来 Web 新特性的报道