React中根据容器尺寸动态隐藏元素实现内容单行不换行
动态隐藏溢出标签保持单行的实现方案
核心逻辑:始终保证容器内容单行不换行,从左到右累加标签宽度,和容器可用宽度(总宽度减去末尾展开按钮宽度)对比,超出范围的靠后标签统一加d-none类隐藏;点击末尾按钮切换全显/单行收起状态。
第一步:先写基础样式消除计算误差
先给容器和子元素加固定布局规则,避免浏览器自动排版影响宽度判断:
.col { white-space: nowrap; overflow: hidden; display: flex; align-items: center; gap: 8px; /* 替换为你项目实际的标签间距 */ } .badge, .show-more { flex-shrink: 0; /* 禁止标签、按钮被flex布局压缩,保证宽度计算准确 */ }
第二步:React核心实现代码
通过useRef获取容器、标签、按钮的实际宽度,用ResizeObserver监听容器尺寸变化,每次尺寸变动/标签列表更新时重新计算可显示的标签数量:
import { useRef, useState, useLayoutEffect } from 'react'; const SingleLineTagList = ({ tagList }) => { const containerRef = useRef(null); const tagItemRefs = useRef([]); const toggleBtnRef = useRef(null); // 单行状态下可见的标签数量 const [visibleTagCount, setVisibleTagCount] = useState(tagList.length); // 是否展开显示全部标签 const [isExpandAll, setIsExpandAll] = useState(false); const calcVisibleTags = () => { if (!containerRef.current || !toggleBtnRef.current) return; // 展开状态下直接显示所有标签,不做截断 if (isExpandAll) { setVisibleTagCount(tagList.length); return; } const containerTotalWidth = containerRef.current.offsetWidth; const btnOccupiedWidth = toggleBtnRef.current.offsetWidth; let currentUsedWidth = btnOccupiedWidth; let canShowCount = 0; // 从第一个标签开始累加宽度,直到超出容器宽度 for (let i = 0; i < tagItemRefs.current.length; i++) { const currentTag = tagItemRefs.current[i]; if (!currentTag) continue; const currentTagWidth = currentTag.offsetWidth; if (currentUsedWidth + currentTagWidth > containerTotalWidth) break; currentUsedWidth += currentTagWidth; canShowCount++; } setVisibleTagCount(canShowCount); }; // 初始化、标签列表更新、展开状态切换时重算,同时监听容器尺寸变化 useLayoutEffect(() => { calcVisibleTags(); const resizeObserver = new ResizeObserver(calcVisibleTags); resizeObserver.observe(containerRef.current); return () => resizeObserver.disconnect(); }, [tagList, isExpandAll]); return ( <div className="row"> <div className="col" ref={containerRef}> {tagList.map((tag, index) => ( <span key={tag.id} ref={dom => tagItemRefs.current[index] = dom} className={`badge bg-white ${index < visibleTagCount || isExpandAll ? '' : 'd-none'}`} > {tag.content} </span> ))} <button ref={toggleBtnRef} type="button" className="btn show-more" onClick={() => setIsExpandAll(!isExpandAll)} > <i className={`fe ${isExpandAll ? 'fe-minus' : 'fe-plus'}`}></i> </button> </div> </div> ); }; export default SingleLineTagList;
实现注意事项
- 宽度计算阶段不要逐个操作DOM增删
d-none类,先算好最终可见标签数量再统一更新状态,避免频繁触发重排影响性能 - 如果标签之间用
margin做间距而非flex的gap,计算累计宽度时要把标签的左右margin值加到对应标签宽度上,否则会出现计算宽度小于实际占用宽度的问题 - 初始计算用
useLayoutEffect而非普通useEffect,可以在浏览器绘制前完成可见数量更新,避免页面加载时标签先换行再隐藏的闪烁问题 - 可以加一层判断:当
visibleTagCount === tagList.length时隐藏末尾的展开按钮,没有溢出标签时不需要显示操作入口
内容的提问来源于stack exchange,提问作者pkzvh
相关产品推荐
相关产品推荐

