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

React使用styled-components实现带左右按钮的导航栏水平滚动

导航栏水平滚动按钮实现方法

两种实现路径都能满足需求,优先推荐无第三方依赖的原生方案,完全匹配你仅使用styled-components的项目要求,不需要额外引入依赖,改造成本极低。

方案1:原生JS + styled-components实现(推荐)

不需要安装任何额外包,直接基于现有代码改造即可,核心逻辑是通过DOM原生的scrollBy方法控制菜单区域滚动,监听滚动位置判断按钮禁用状态。

  • 首先调整导航的DOM结构,拆分为外层容器、左右控制按钮、可滚动菜单区三个部分,所有样式用styled-components编写:
import styled from 'styled-components';
import { useState, useRef, useEffect } from 'react';

// 导航最外层容器
const NavWrapper = styled.div`
  display: flex;
  align-items: center;
  width: 100%;
  position: relative;
  background: #fff;
  border-bottom: 1px solid #eee;
`;

// 通用滚动按钮样式
const ScrollBtn = styled.button`
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  border: none;
  border-radius: 50%;
  background: #f5f5f5;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 10;
  &:disabled {
    opacity: 0.3;
    cursor: not-allowed;
  }
`;

// 可滚动菜单区域
const NavMenuList = styled.div`
  flex: 1;
  display: flex;
  gap: 24px;
  overflow-x: auto;
  scroll-behavior: smooth;
  /* 隐藏默认滚动条 */
  &::-webkit-scrollbar {
    display: none;
  }
  -ms-overflow-style: none;
  scrollbar-width: none;
  padding: 0 12px;
`;

// 原有导航选项卡,加不收缩属性即可
const NavTab = styled.div`
  flex-shrink: 0;
  padding: 12px 0;
  cursor: pointer;
  /* 保留你原来写的选中态、hover等样式,不需要改动 */
`;
  • 补充滚动交互逻辑,直接在组件里写即可:
const Navigation = () => {
  const menuRef = useRef(null);
  // 控制左右按钮的禁用状态
  const [isLeftDisabled, setIsLeftDisabled] = useState(true);
  const [isRightDisabled, setIsRightDisabled] = useState(false);

  // 实时更新按钮状态:滚到最左禁用左按钮,滚到最右禁用右按钮
  const updateBtnStatus = () => {
    const menu = menuRef.current;
    if (!menu) return;
    setIsLeftDisabled(menu.scrollLeft === 0);
    setIsRightDisabled(menu.scrollLeft + menu.clientWidth >= menu.scrollWidth - 1);
  };

  useEffect(() => {
    const menu = menuRef.current;
    if (!menu) return;
    updateBtnStatus();
    menu.addEventListener('scroll', updateBtnStatus);
    window.addEventListener('resize', updateBtnStatus);
    return () => {
      menu.removeEventListener('scroll', updateBtnStatus);
      window.removeEventListener('resize', updateBtnStatus);
    };
  }, []);

  // 点击按钮滚动固定距离,数值可以根据需求调整
  const handleScroll = (direction) => {
    const menu = menuRef.current;
    if (!menu) return;
    const scrollDistance = 200;
    menu.scrollBy({
      left: direction === 'left' ? -scrollDistance : scrollDistance,
      behavior: 'smooth'
    });
  };

  return (
    <NavWrapper>
      <ScrollBtn 
        onClick={() => handleScroll('left')} 
        disabled={isLeftDisabled}
      >
        {/* 替换成你自己的左箭头图标即可 */}
        {'<'}
      </ScrollBtn>
      <NavMenuList ref={menuRef}>
        {/* 这里直接放你原来写的路由选项卡遍历逻辑,不需要改动 */}
        {tabs.map(tab => <NavTab key={tab.path}>{tab.name}</NavTab>)}
      </NavMenuList>
      <ScrollBtn 
        onClick={() => handleScroll('right')} 
        disabled={isRightDisabled}
      >
        {/* 替换成你自己的右箭头图标即可 */}
        {'>'}
      </ScrollBtn>
    </NavWrapper>
  )
};

你的原有路由跳转、选项卡选中逻辑完全不需要改动,套上外层结构就能用。

方案2:使用react-horizontal-scrolling-menu实现

如果不想自己写边界判断逻辑,可以用这个包实现,样式依然全部用styled-components控制:

  1. 先安装依赖:
    npm i react-horizontal-scrolling-menu
  2. 封装组件,覆盖包的默认样式即可:
import { ScrollMenu, VisibilityContext } from 'react-horizontal-scrolling-menu';
import styled from 'styled-components';
import { useContext } from 'react';

// 样式和方案1保持一致即可
const LeftBtn = styled.button`/* 左按钮样式 */`;
const RightBtn = styled.button`/* 右按钮样式 */`;
const MenuWrapper = styled.div`/* 导航外层样式 */`;
const TabItem = styled.div`/* 选项卡原有样式 */`;

// 自定义左箭头
const LeftArrow = () => {
  const { scrollPrev, isFirstItemVisible } = useContext(VisibilityContext);
  return (
    <LeftBtn onClick={() => scrollPrev()} disabled={isFirstItemVisible}>
      {'<'}
    </LeftBtn>
  )
};

// 自定义右箭头
const RightArrow = () => {
  const { scrollNext, isLastItemVisible } = useContext(VisibilityContext);
  return (
    <RightBtn onClick={() => scrollNext()} disabled={isLastItemVisible}>
      {'>'}
    </RightBtn>
  )
};

const Navigation = () => {
  return (
    <MenuWrapper>
      <ScrollMenu LeftArrow={LeftArrow} RightArrow={RightArrow}>
        {tabs.map(tab => <TabItem key={tab.path} itemId={tab.path}>{tab.name}</TabItem>)}
      </ScrollMenu>
    </MenuWrapper>
  )
};

小提示:如果你的导航选项卡是接口异步返回的,等数据渲染完成后记得手动触发一次按钮状态更新,避免边界判断不准。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 11:21:43