You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用CSS Sticky属性时滚动标题无法固定的问题排查

日历视图粘性标题失效问题修复

问题描述

我实现了一个简易日历React组件,父容器通过flex布局让timeslot和days容器并排显示,设置了固定尺寸并允许内容滚动。但滚动时,timeHeader和dayHeader在垂直滚动约40%后就不再固定,无法达到预期效果。

期望效果

  • Time Header始终固定在顶部
  • Day Header垂直滚动时保持固定
  • 时间项(如7:00 a.m)水平滚动时保持固定

现有代码

React组件代码

import React from "react";
import "./Test.css";

function Test() {
    return (
        <div className="parent">
            <div className="timeslot">
                <div className="timeHeader">Time</div>
                <div>
                    <ul>
                        <li>7:00 a.m</li>
                        <li>8:00 a.m</li>
                        <li>9:00 a.m</li>
                        <li>10:00 a.m</li>
                        <li>11:00 a.m</li>
                        <li>12:00 p.m</li>
                        <li>1:00 p.m</li>
                        <li>2:00 p.m</li>
                        <li>3:00 p.m</li>
                        <li>4:00 p.m</li>
                        <li>5:00 p.m</li>
                        <li>6:00 p.m</li>
                        <li>7:00 p.m</li>
                        <li>8:00 p.m</li>
                        <li>9:00 p.m</li>
                        <li>10:00 p.m</li>
                    </ul>
                </div>
            </div>
            <div className="days">
                <div className="dayHeader">Days</div>
                <div className="dayslots">
                    <div>
                        <ul>
                            <li>1</li>
                            <li>1</li>
                            <li>1</li>
                            <li>1</li>
                            <li>1</li>
                            <li>1</li>
                            <li>1</li>
                            <li>1</li>
                            <li>1</li>
                            <li>1</li>
                            <li>1</li>
                            <li>1</li>
                            <li>1</li>
                            <li>1</li>
                            <li>1</li>
                            <li>1</li>
                        </ul>
                    </div>
                    <div>
                        <ul>
                            <li>2</li>
                            <li>2</li>
                            <li>2</li>
                            <li>2</li>
                            <li>2</li>
                            <li>2</li>
                            <li>2</li>
                            <li>2</li>
                            <li>2</li>
                            <li>2</li>
                            <li>2</li>
                            <li>2</li>
                            <li>2</li>
                            <li>2</li>
                            <li>2</li>
                            <li>2</li>
                        </ul>
                    </div>
                    <div>
                        <ul>
                            <li>3</li>
                            <li>3</li>
                            <li>3</li>
                            <li>3</li>
                            <li>3</li>
                            <li>3</li>
                            <li>3</li>
                            <li>3</li>
                            <li>3</li>
                            <li>3</li>
                            <li>3</li>
                            <li>3</li>
                            <li>3</li>
                            <li>3</li>
                        </ul>
                    </div>
                    <div>
                        <ul>
                            <li>4</li>
                            <li>4</li>
                            <li>4</li>
                            <li>4</li>
                            <li>4</li>
                            <li>4</li>
                            <li>4</li>
                            <li>4</li>
                            <li>4</li>
                            <li>4</li>
                            <li>4</li>
                            <li>4</li>
                            <li>4</li>
                            <li>4</li>
                            <li>4</li>
                            <li>4</li>
                        </ul>
                    </div>
                </div>
            </div>
        </div>
    );
}

export default Test;

原CSS代码

.parent {
    display: flex;
    width: 400px;
    height: 100px;
    overflow: scroll;
}

.timeHeader {
    position: sticky;
    top: 0;
    background-color: white;
}

.timeslot {
    position: sticky;
    left: 0;
    z-index: 2;
    background: white;
}

.days {
    flex: 5;
}

li {
    min-width: 100px;
}

.dayslots {
    display: flex;
}

.dayHeader {
    background-color: white;
    position: sticky;
    top: 0;
}

问题根源

position: sticky的粘性效果是相对于最近的滚动祖先元素生效的。原代码中.parent是唯一的滚动容器,而.timeslot和.days作为flex子元素,高度被限制为和.parent一致,导致sticky标题的可粘区域仅为.parent的高度,滚动超过这个范围后标题就会跟随内容滚动。

修复方案

通过拆分滚动上下文,让标题所在的容器和内容容器分别处理滚动,确保sticky元素的粘性范围覆盖全部内容:

修改后的CSS代码

.parent {
    display: flex;
    width: 400px;
    height: 300px; /* 调高容器高度,提升体验 */
    overflow: hidden; /* 父容器隐藏溢出,由子容器处理滚动 */
}

.timeslot {
    position: sticky;
    left: 0;
    z-index: 2;
    background: white;
    height: 100%;
    display: flex;
    flex-direction: column; /* 垂直布局标题和时间列表 */
}

/* 让时间列表单独垂直滚动 */
.timeslot > div:last-child {
    overflow-y: auto;
}

.timeHeader {
    position: sticky;
    top: 0;
    background-color: white;
    padding: 4px;
    border-bottom: 1px solid #eee; /* 添加边框区分标题 */
}

.days {
    flex: 1;
    height: 100%;
    display: flex;
    flex-direction: column; /* 垂直布局标题和天内容 */
}

.dayHeader {
    position: sticky;
    top: 0;
    background-color: white;
    padding: 4px;
    border-bottom: 1px solid #eee; /* 添加边框区分标题 */
}

.dayslots {
    display: flex;
    overflow-y: auto; /* 天内容垂直滚动 */
    overflow-x: auto; /* 天内容水平滚动 */
}

li {
    min-width: 100px;
    padding: 4px;
    border-bottom: 1px solid #eee; /* 添加边框优化视觉 */
}

ul {
    margin: 0;
    padding: 0;
    list-style: none; /* 移除默认列表样式 */
}

修复说明

  1. 父容器设置overflow: hidden,避免自身成为滚动上下文干扰sticky元素
  2. .timeslot和.days采用垂直flex布局,将标题与内容分离,各自处理滚动
  3. 时间列表和天内容容器分别设置滚动属性,确保内容可滚动的同时,标题的粘性范围覆盖全部内容
  4. 添加边框和内边距优化视觉体验

内容的提问来源于stack exchange,提问作者McQueen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 15:45:11