最近前端圈子里聊得最嗨的就是CSS一堆新玩意儿,从
:has()到锚点定位,从滚动时间线到自动高度过渡,简直像开了挂。以前写个手风琴菜单还得靠JS偷摸算高度,现在几行CSS直接搞定,再也不用担心动画翻车。这篇文章就掰扯掰扯这些新特性到底咋用,顺便带大伙过一遍实际干活时碰到的坑和骚操作。
啥是CSS父选择器
:has()这玩意儿就像个透视眼,能根据后代元素来选中父级。以前想给包含图片的卡片加个特殊边框?得JS遍历。现在:has()一出马,直接.card:has(img)就搞定。这货兼容性已经稳了,各大浏览器全支持,放心冲。
自动高度过渡咋整
过渡到height: auto曾经是CSS的噩梦,现在用grid配合grid-template-rows就能丝滑搞定。下面这套流程专治各种折叠面板翻车。
折叠面板平滑开合
第一步:搭骨架
写一个HTML结构,外层是触发按钮,内层是内容区。按钮负责切换一个open类,内容区包着真正的文本或图片。
<div class="collapse">
<button class="collapse__btn">戳我展开</button>
<div class="collapse__content">
<p>这里随便塞点东西,文字、图片、视频都行,高度会自己变。</p>
</div>
</div>第二步:CSS关键三板斧
内容区先设置display: grid,然后grid-template-rows从0fr过渡到1fr。注意要搭配overflow: hidden防止内容溢出。
.collapse__content {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 0.3s ease;
overflow: hidden;
}
.collapse.open .collapse__content {
grid-template-rows: 1fr;
}
/* 内层必须限制最小高度0,否则1fr不生效 */
.collapse__content > * {
min-height: 0;
}第三步:JS只负责切类
别在JS里算高度,直接给父级.collapse toggle一下.open类。
document.querySelector('.collapse__btn').addEventListener('click', (e) => {
e.currentTarget.closest('.collapse').classList.toggle('open');
});敲黑板:内层那个min-height: 0是灵魂,少了它1fr会变成自动撑开,过渡直接消失。另外grid-template-rows过渡只支持fr和百分比,别想着用auto。这套方案在移动端丝滑得一匹,比max-height猜大小靠谱多了。
锚点定位粘性菜单
锚点定位能搞出一个固定在某个元素旁边的浮层,滚动时还跟屁虫一样跟着。写一个评论按钮旁边弹出工具条的案例,全程不用position的top/left苦算偏移。
跟屁虫工具条
第一步:锚点关联
按钮用anchor-name起个名,工具条用position-anchor绑定这个名。
<button class="reply-btn" style="anchor-name: --reply">留言</button>
<div class="toolbar" style="position-anchor: --reply">👍 ❤️ 😂</div>第二步:定位属性
工具条设置position: absolute或fixed,然后用anchor()函数指定位置。
.toolbar {
position: fixed;
bottom: anchor(--reply top);
left: anchor(--reply right);
margin-left: 8px;
background: white;
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
border-radius: 20px;
padding: 4px 12px;
}第三步:滚动时自动跟随
因为用了position: fixed,页面滚动时锚点元素移动,工具条会自动重新计算位置。如果要用在滚动容器内,改成position: absolute配合相对定位的父级。
这里容易踩坑:锚点名称必须以--开头,像变量那样。anchor()函数第一个参数是锚点名,第二个是方向(top/right/bottom/left)。如果工具条被裁切了,可以加anchor-scroll属性调整行为。实测这个特性在Chromium内核已经稳了,Safari也在路上了。
容器查询响应式卡片
容器查询让组件可以根据父容器宽度变样式,而不是看视口。写一个卡片列表,卡片在窄容器里变竖排,在宽容器里变横排,不用媒体查询。
智能卡片布局
第一步:容器声明
给每个卡片的父级加上container-type: inline-size,名字随便起。
<div class="card-list">
<div class="card-container" style="container-type: inline-size; container-name: cardbox">
<div class="card">
<img src="avatar.png">
<div class="card__info">...</div>
</div>
</div>
<!-- 重复多个卡片 -->
</div>第二步:容器查询规则
当card-container宽度大于300px时,卡片变成横向排列。
@container cardbox (min-width: 300px) {
.card {
display: flex;
flex-direction: row;
align-items: center;
}
.card img {
width: 80px;
height: 80px;
margin-right: 16px;
}
}第三步:降级处理
没支持的浏览器会直接忽略@container,默认样式得写在外面。
.card {
display: flex;
flex-direction: column;
}实际开发时,container-name别起太长的名,容易写错。而且容器查询不认display: contents的父级,那种父级会被忽略。这个特性现在三大浏览器都支持了,放心用。
多方案解决弹窗自动定位
有时候想搞一个点击按钮后,在按钮旁边弹出菜单,还要跟着滚动走。对比两套方案:锚点定位 vs 老派JS计算偏移。
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 锚点定位 | 代码少 滚动自动跟 | 兼容性稍窄 | 现代项目 无IE |
| JS计算 | 兼容性好 全平台 | 代码多 滚动要监听 | 老旧系统 |
锚点定位代码量:上面已经演示过,十几行CSS搞定。
JS计算方案:需要getBoundingClientRect,加滚动事件,还要防抖,一不留神就掉帧。
// 老派做法(演示用,实际项目请用锚点)
function updateToolbarPos() {
const btn = document.querySelector('.reply-btn');
const rect = btn.getBoundingClientRect();
toolbar.style.top = rect.bottom + 'px';
toolbar.style.left = rect.left + 'px';
}
window.addEventListener('scroll', () => requestAnimationFrame(updateToolbarPos));
window.addEventListener('resize', updateToolbarPos);两相对比,新特性简直降维打击。除非要兼容古董浏览器,否则直接上锚点定位,省心又流畅。
滚动驱动动画
滚动时间线能让动画跟着滚动进度走,比如一个进度条随着页面滚动变宽。这玩意儿以前得写scroll事件算百分比,现在CSS一肩扛。
核心代码:
.progress {
height: 4px;
background: blue;
transform-origin: left;
animation: fill 1s linear forwards;
animation-timeline: scroll();
}
@keyframes fill {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}注意animation-timeline目前还在实验阶段,Chromium可以开flag尝鲜。实际项目可以用scroll-timeline配合view-timeline做更精细的控制。不过这货变化快,建议先用Intersection Observer做降级。
