React分类切换时实现水平滚动条自动滚动的方法
分类切换自动水平滚动实现方案
核心逻辑是通过DOM ref拿到滚动容器和激活分类项的实例,在激活分类变化时调用原生滚动API,自动将激活项移动到可视区域内,全程不需要引入第三方依赖。
实现步骤
- 引入需要的React hooks
import { useRef, useEffect, forwardRef } from 'react';
- 声明ref存储DOM实例
在分类组件内创建两个ref,分别绑定外层滚动容器、所有分类项的DOM节点:
// 绑定水平滚动容器 const categoryScrollRef = useRef(null); // 按索引存储每个分类项的DOM const categoryItemRefs = useRef([]);
- 给MobileCategory组件添加ref转发
自定义组件默认不能直接接收ref属性,需要用forwardRef做一层透传,把ref绑定到分类项最外层的DOM节点上:
const MobileCategory = forwardRef(({ categoryName, active, setActiveCategory, id }, ref) => { return ( <div ref={ref} onClick={() => setActiveCategory(id)} className={`px-3 py-1 whitespace-nowrap ${active ? 'text-red-500 font-bold border-b-2 border-red-500' : 'text-gray-600'}`} > {categoryName} </div> ) })
- 给滚动容器和分类项绑定ref
修改原有渲染代码,把ref绑定到对应节点上:
<div ref={categoryScrollRef} className="bg-white p-2 sticky top-0 flex space-x-4 overflow-x-auto overflow-y-hidden border-b border-gray-300 z-10" > {categories?.map((item, index) => { const isActive = item.Id.toString() === activeCategory; return ( <MobileCategory key={item.Id} ref={(el) => (categoryItemRefs.current[index] = el)} categoryName={item.Title} active={isActive} setActiveCategory={setActiveCategory} id={item.Id.toString()} /> ); })} </div>
提示:把原有overflow-scroll改成overflow-x-auto overflow-y-hidden可以避免垂直方向出现不必要的滚动条。
- 监听激活分类变化触发滚动
用useEffect监听activeCategory变化,激活项更新时调用scrollIntoView实现平滑滚动:
useEffect(() => { if (!categories || !categoryScrollRef.current) return; // 找到当前激活分类的索引 const activeIndex = categories.findIndex(item => item.Id.toString() === activeCategory); const activeItem = categoryItemRefs.current[activeIndex]; if (activeIndex < 0 || !activeItem) return; // 触发滚动 activeItem.scrollIntoView({ behavior: 'smooth', // 平滑滚动,改成auto就是瞬间跳转 block: 'nearest', inline: 'center' // 对齐方式:start=靠左、center=居中、end=靠右、nearest=最小滚动距离 }); }, [activeCategory, categories]);
可选自定义调整
- 如果需要自定义滚动偏移量(比如让激活项离容器左侧留16px间距),可以不用
scrollIntoView,手动计算偏移量调用容器的滚动方法:
// 替换上面useEffect里的scrollIntoView逻辑 const container = categoryScrollRef.current; const targetLeft = activeItem.offsetLeft - container.offsetLeft - 16; container.scrollTo({ left: targetLeft, behavior: 'smooth' });
- 如果需要隐藏滚动条但保留滚动能力,可以在全局CSS中添加以下样式,再给滚动容器加上
scrollbar-hide类名:
.scrollbar-hide::-webkit-scrollbar { display: none; } .scrollbar-hide { -ms-overflow-style: none; scrollbar-width: none; }
内容的提问来源于stack exchange,提问作者Caner
相关产品推荐
相关产品推荐

