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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 13:21:31