如何实现滚动时顶部固定div被下方div顶出的效果(仿Telegram日期栏)
实现Telegram式聊天日期栏效果的方案
能不能只用CSS实现?
不行。CSS的position: sticky只能让单个元素在容器内固定,当新的日期栏滚动到顶部时,会直接覆盖在旧日期栏上方,做不到“顶出”的平移过渡效果,必须结合JavaScript来控制元素的位置和动画逻辑。
具体实现方案(含JS示例)
核心思路是监听滚动事件,实时判断每个日期栏的位置:当旧日期栏固定在顶部时,新日期栏接近顶部时,让旧日期栏向上滑出视口,同时将新日期栏固定到顶部;向上滚动时则恢复上一个日期栏的固定状态。
以下是完整示例代码:
HTML结构
<div class="chat-container"> <!-- 4月18日聊天组 --> <div class="date-divider" data-date="4月18日">4月18日</div> <div class="chat-message">今天天气不错</div> <div class="chat-message">要不要一起吃饭?</div> <!-- 4月16日聊天组 --> <div class="date-divider" data-date="4月16日">4月16日</div> <div class="chat-message">昨天的会议记录发我一下</div> <div class="chat-message">好的,马上发你</div> <!-- 4月15日聊天组 --> <div class="date-divider" data-date="4月15日">4月15日</div> <div class="chat-message">周末打算去哪玩?</div> <div class="chat-message">还没想好,你有推荐吗?</div> <div class="chat-message">可以去郊外露营,挺不错的</div> <!-- 4月14日聊天组 --> <div class="date-divider" data-date="4月14日">4月14日</div> <div class="chat-message">今天加班到很晚</div> <div class="chat-message">注意休息,别太累了</div> </div>
CSS样式
.chat-container { max-width: 400px; margin: 0 auto; padding: 20px 0; height: 500px; overflow-y: auto; border: 1px solid #eee; } .date-divider { text-align: center; padding: 8px 0; background: #f5f5f5; color: #666; margin: 10px 0; border-radius: 4px; position: relative; transition: transform 0.3s ease; z-index: 10; } .date-divider.fixed { position: fixed; top: 0; left: 50%; transform: translateX(-50%); width: 400px; margin: 0; } .chat-message { padding: 10px 15px; margin: 5px 0; background: #e5e5ea; border-radius: 18px; max-width: 70%; margin-left: auto; }
JavaScript逻辑
const chatContainer = document.querySelector('.chat-container'); const dateDividers = document.querySelectorAll('.date-divider'); let currentFixedDivider = null; chatContainer.addEventListener('scroll', () => { const containerTop = chatContainer.getBoundingClientRect().top; dateDividers.forEach(divider => { const rect = divider.getBoundingClientRect(); // 计算日期栏相对于容器顶部的位置 const relativeTop = rect.top - containerTop; // 当日期栏即将滚动到容器顶部时 if (relativeTop <= 0 && !divider.classList.contains('fixed')) { if (currentFixedDivider) { // 让当前固定的日期栏向上滑出视口 const slideOutDistance = currentFixedDivider.offsetHeight; currentFixedDivider.style.transform = `translateX(-50%) translateY(-${slideOutDistance}px)`; // 动画结束后清除固定状态 setTimeout(() => { currentFixedDivider.classList.remove('fixed'); currentFixedDivider.style.transform = ''; }, 300); } // 固定当前日期栏到顶部 divider.classList.add('fixed'); currentFixedDivider = divider; } // 向上滚动时,恢复上一个日期栏的固定状态 if (relativeTop > divider.offsetHeight && divider.classList.contains('fixed')) { divider.classList.remove('fixed'); const prevDivider = divider.previousElementSibling?.closest('.date-divider'); if (prevDivider) { prevDivider.classList.add('fixed'); currentFixedDivider = prevDivider; } else { currentFixedDivider = null; } } }); }); // 初始化时触发一次滚动检查 chatContainer.dispatchEvent(new Event('scroll'));
效果说明
- 向下滚动查看旧消息时,当前日期栏会固定在容器顶部;
- 新日期栏滚动到容器顶部时,旧日期栏会平滑向上滑出,新日期栏随即固定到顶部;
- 向上滚动时,会自动恢复上一个日期栏的固定状态。
内容的提问来源于stack exchange,提问作者Sarraayush
相关产品推荐
相关产品推荐

