实现带粘性日期头部的双栏任务列表滚动交互的技术问询
实现双栏日期关联的粘性待办列表
核心需求
实现双栏布局的待办任务列表:
- 左侧显示当前日期框,右侧按日期分组展示任务
- 左侧日期框需跟随对应日期的首个
<li>元素滚动 - 当日期框滚动到容器顶部时,切换为粘性固定,直到下一个日期组的首个
<li>滚动上来替换它
实现步骤:关联左侧日期框与右侧<li>元素
1. 标记关键元素
给每个日期组的**首个<li>**添加data-date属性,明确对应日期;同时在左侧准备日期容器,用于动态切换日期内容:
<!-- 右侧列表修改 --> <li data-date="1/01/2025"><span>List Item #1</span><span class="date">1/01/2025</span></li> <!-- ...其他同日期项 --> <li data-date="2/01/2025"><span>List Item #6</span><span class="date">2/01/2025</span></li> <!-- ...其他同日期项 --> <!-- 左侧容器修改 --> <div id="leftColumn"> <div class="date-box" id="stickyDate">1/01/2025</div> </div>
2. 预处理日期组信息
通过JS收集所有日期组的首个<li>元素,记录它们的偏移量和对应日期:
const rightColumn = document.getElementById('rightColumn'); const stickyDate = document.getElementById('stickyDate'); const dateHeaders = Array.from(document.querySelectorAll('li[data-date]')); // 存储每个日期组的关键信息:元素、偏移量、日期 const dateGroups = dateHeaders.map(el => ({ element: el, topOffset: el.offsetTop, date: el.dataset.date }));
3. 监听滚动,实现跟随与粘性逻辑
在滚动事件中,判断当前可视区域内的日期组,切换左侧日期框的内容和定位:
rightColumn.addEventListener('scroll', () => { const scrollTop = rightColumn.scrollTop; const containerTop = rightColumn.offsetTop; // 找到当前处于顶部的日期组 let currentDate = dateGroups[0].date; let targetOffset = dateGroups[0].topOffset; let shouldStick = false; for (let i = dateGroups.length - 1; i >= 0; i--) { const group = dateGroups[i]; // 当元素顶部进入容器可视区域时 if (group.topOffset <= scrollTop) { currentDate = group.date; targetOffset = group.topOffset; // 判断是否需要切换为粘性定位 shouldStick = scrollTop >= targetOffset; break; } } // 更新日期内容 stickyDate.textContent = currentDate; // 切换定位方式:跟随元素/粘性固定 if (shouldStick) { stickyDate.style.position = 'fixed'; stickyDate.style.top = `${containerTop}px`; } else { stickyDate.style.position = 'absolute'; stickyDate.style.top = `${targetOffset + containerTop}px`; } });
4. 调整CSS适配逻辑
修改左侧列和日期框的样式,确保定位正确:
#leftColumn { background-color: #fafafa; position: relative; /* 作为绝对定位的父容器 */ overflow: hidden; } #rightColumn { background-color: #fff; overflow-y: auto; /* 开启垂直滚动 */ } .date-box { width: 100px; height: 80px; background-color: #ccc; display: flex; justify-content: center; align-items: center; font-size: 18px; border-radius: 5px; color: #333; left: 20px; /* 与左侧列padding对齐 */ }
完整代码示例
HTML(含内嵌CSS与JS)
<!DOCTYPE html> <html lang="en"> <head> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> * { box-sizing: border-box; margin: 0; padding: 0; } body { display: flex; justify-content: center; align-items: center; min-height: 100vh; font-family: Arial, sans-serif; background-color: #f0f0f0; } .container { display: flex; width: 80%; height: 80vh; border: 0px solid #ccc; } #leftColumn, #rightColumn { flex: 1; padding: 20px; border: 0px solid #ddd; height: 100%; } #leftColumn { background-color: #fafafa; position: relative; overflow: hidden; } #rightColumn { background-color: #fff; overflow-y: auto; } ul { list-style-type: none; } li { height: 80px; margin-bottom: 10px; display: flex; justify-content: center; align-items: center; background-color: #e0e0e0; border-radius: 5px; font-size: 18px; } .date { font-size: 14px; color: #666; margin-left: 10px; } .date-box { width: 100px; height: 80px; background-color: #ccc; display: flex; justify-content: center; align-items: center; font-size: 18px; border-radius: 5px; color: #333; left: 20px; } </style> </head> <body> <div class="container"> <div id="leftColumn"> <div class="date-box" id="stickyDate">1/01/2025</div> </div> <div id="rightColumn"> <ul> <!-- Items with date 1/01/2025 --> <li data-date="1/01/2025"><span>List Item #1</span><span class="date">1/01/2025</span></li> <li><span>List Item #2</span><span class="date">1/01/2025</span></li> <li><span>List Item #3</span><span class="date">1/01/2025</span></li> <li><span>List Item #4</span><span class="date">1/01/2025</span></li> <li><span>List Item #5</span><span class="date">1/01/2025</span></li> <!-- Items with date 2/01/2025 --> <li data-date="2/01/2025"><span>List Item #6</span><span class="date">2/01/2025</span></li> <li><span>List Item #7</span><span class="date">2/01/2025</span></li> <li><span>List Item #8</span><span class="date">2/01/2025</span></li> <li><span>List Item #9</span><span class="date">2/01/2025</span></li> <li><span>List Item #10</span><span class="date">2/01/2025</span></li> <!-- Items with date 3/01/2025 --> <li data-date="3/01/2025"><span>List Item #11</span><span class="date">3/01/2025</span></li> <li><span>List Item #12</span><span class="date">3/01/2025</span></li> <li><span>List Item #13</span><span class="date">3/01/2025</span></li> <li><span>List Item #14</span><span class="date">3/01/2025</span></li> <li><span>List Item #15</span><span class="date">3/01/2025</span></li> </ul> </div> </div> <script> const rightColumn = document.getElementById('rightColumn'); const stickyDate = document.getElementById('stickyDate'); const dateHeaders = Array.from(document.querySelectorAll('li[data-date]')); const dateGroups = dateHeaders.map(el => ({ element: el, topOffset: el.offsetTop, date: el.dataset.date })); // 初始化位置 stickyDate.style.top = `${dateGroups[0].topOffset + rightColumn.offsetTop}px`; stickyDate.style.position = 'absolute'; rightColumn.addEventListener('scroll', () => { const scrollTop = rightColumn.scrollTop; const containerTop = rightColumn.offsetTop; let currentDate = dateGroups[0].date; let targetOffset = dateGroups[0].topOffset; let shouldStick = false; for (let i = dateGroups.length - 1; i >= 0; i--) { const group = dateGroups[i]; if (group.topOffset <= scrollTop) { currentDate = group.date; targetOffset = group.topOffset; shouldStick = scrollTop >= targetOffset; break; } } stickyDate.textContent = currentDate; if (shouldStick) { stickyDate.style.position = 'fixed'; stickyDate.style.top = `${containerTop}px`; } else { stickyDate.style.position = 'absolute'; stickyDate.style.top = `${targetOffset + containerTop}px`; } }); </script> </body> </html>
关键逻辑说明
- 元素标记:通过
data-date绑定日期组的首个<li>和左侧日期框的内容关联 - 滚动监听:实时检测右侧列表的滚动位置,匹配当前可视区域内的日期组
- 定位切换:根据元素偏移量判断是让日期框跟随对应
<li>,还是固定在容器顶部
内容的提问来源于stack exchange,提问作者Cainnech
相关产品推荐
相关产品推荐

