如何在Breadcrumb组件中点击省略号显示隐藏菜单?
实现带省略号弹窗的面包屑组件
核心逻辑拆解
先判断面包屑是否需要截断:计算所有菜单项总宽度与容器宽度的差值,当总宽度超出容器时,触发截断逻辑,仅保留第一项、倒数第二项、最后一项,中间用可点击的省略号占位。
具体实现步骤
1. 菜单项截断处理
- 获取面包屑容器的可用宽度,以及每个菜单项的实际宽度(通过
getBoundingClientRect()获取) - 累加菜单项宽度,直到加上后续项会超出容器宽度,确定需要隐藏的项范围
- 若总项数≤3,直接显示全部;否则组装成「第一项 + 省略号 + 倒数第二项 + 最后一项」的结构
示例代码片段(React 场景):
import { useRef, useEffect, useState } from 'react'; const Breadcrumb = ({ items }) => { const breadcrumbRef = useRef(null); const [truncatedItems, setTruncatedItems] = useState(items); const [hiddenItems, setHiddenItems] = useState([]); const calculateTruncation = () => { const container = breadcrumbRef.current; if (!container || items.length <= 3) { setTruncatedItems(items); setHiddenItems([]); return; } const containerWidth = container.offsetWidth; let totalWidth = 0; const visible = [items[0]]; const hidden = []; // 计算第一项宽度 const firstEl = container.querySelector('.breadcrumb-item:first-child'); totalWidth += firstEl?.offsetWidth || 0; // 计算最后两项总宽度 const lastTwoEls = Array.from(container.querySelectorAll('.breadcrumb-item:last-child, .breadcrumb-item:nth-last-child(2)')); const lastTwoWidth = lastTwoEls.reduce((sum, el) => sum + (el?.offsetWidth || 0), 0); const ellipsisWidth = 30; // 预留省略号宽度 // 遍历中间项,判断是否能容纳 for (let i = 1; i < items.length - 2; i++) { const el = container.querySelectorAll('.breadcrumb-item')[i]; const itemWidth = el?.offsetWidth || 0; if (totalWidth + itemWidth + lastTwoWidth + ellipsisWidth < containerWidth) { visible.push(items[i]); totalWidth += itemWidth; } else { hidden.push(items[i]); } } // 组装最终显示项 setTruncatedItems(hidden.length ? [...visible, { type: 'ellipsis' }, ...items.slice(-2)] : items); setHiddenItems(hidden); }; // 初始化+窗口 resize 时重新计算(防抖处理) useEffect(() => { calculateTruncation(); const debounceResize = setTimeout(() => calculateTruncation(), 200); window.addEventListener('resize', debounceResize); return () => window.removeEventListener('resize', debounceResize); }, [items]); // 渲染菜单项 const renderItems = () => { return truncatedItems.map((item, idx) => { if (item.type === 'ellipsis') { return <EllipsisPopup hiddenItems={hiddenItems} key="ellipsis" />; } return ( <div key={item.key} className="breadcrumb-item" onClick={() => item.onClick?.()}> {item.label} </div> ); }); }; return <div ref={breadcrumbRef} className="breadcrumb-container">{renderItems()}</div>; };
2. 省略号弹窗实现
- 给省略号绑定点击事件,控制弹窗的显示/隐藏状态
- 弹窗内渲染所有隐藏菜单项,保留原有的跳转逻辑
- 动态定位弹窗:基于省略号元素的位置,设置弹窗的
top和left,确保跟随省略号显示
示例省略号弹窗组件:
const EllipsisPopup = ({ hiddenItems }) => { const [showModal, setShowModal] = useState(false); const ellipsisRef = useRef(null); const modalRef = useRef(null); const handleClick = () => { setShowModal(true); // 定位弹窗 if (ellipsisRef.current && modalRef.current) { const rect = ellipsisRef.current.getBoundingClientRect(); modalRef.current.style.top = `${rect.bottom + window.scrollY}px`; modalRef.current.style.left = `${rect.left + window.scrollX}px`; } }; return ( <> <div ref={ellipsisRef} className="breadcrumb-ellipsis" onClick={handleClick}> ... </div> {showModal && ( <div className="breadcrumb-modal" onClick={() => setShowModal(false)}> <div className="modal-content" onClick={(e) => e.stopPropagation()}> {hiddenItems.map(item => ( <div key={item.key} className="modal-item" onClick={() => { setShowModal(false); item.onClick?.(); }} > {item.label} </div> ))} </div> </div> )} </> ); };
3. 关键样式设置
.breadcrumb-container { display: flex; gap: 8px; overflow: hidden; align-items: center; } .breadcrumb-item { flex-shrink: 0; cursor: pointer; padding: 0 4px; } .breadcrumb-ellipsis { cursor: pointer; padding: 0 4px; user-select: none; } .breadcrumb-modal { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 1000; background: rgba(0,0,0,0.1); } .modal-content { position: absolute; background: #fff; border-radius: 4px; box-shadow: 0 2px 12px rgba(0,0,0,0.15); padding: 8px 0; min-width: 160px; } .modal-item { padding: 8px 16px; cursor: pointer; } .modal-item:hover { background: #f5f5f5; }
边界情况处理
- 当菜单项总数≤3时,不显示省略号,直接展示全部
- 窗口大小改变时,防抖触发重新计算截断逻辑
- 点击弹窗外部区域自动关闭弹窗
内容的提问来源于stack exchange,提问作者Katyellen
相关产品推荐
相关产品推荐

