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

React MUI Autocomplete多选时如何根据空间自动限制显示标签数量?

实现MUI Autocomplete自适应一行的标签显示(多选)

可以实现这个需求,核心思路是动态计算失焦状态下输入容器可容纳的标签数量,替代固定的limitTags。以下是具体实现方案:

实现步骤

  1. 监听组件的聚焦/失焦状态,仅在失焦时计算可显示标签数
  2. 获取Autocomplete输入容器的宽度,预留输入占位符的空间
  3. 通过临时元素模拟标签样式,计算每个选中标签的实际宽度,累加判断可容纳数量
  4. 自定义renderTags方法,根据计算结果渲染标签和剩余数量提示

完整代码示例

import React, { useRef, useState, useEffect, useCallback } from 'react';
import Autocomplete from '@mui/material/Autocomplete';
import TextField from '@mui/material/TextField';
import Chip from '@mui/material/Chip';

const top100Films = [
  { title: 'The Shawshank Redemption', year: 1994 },
  { title: 'The Godfather', year: 1972 },
  { title: 'The Godfather: Part II', year: 1974 },
  { title: 'The Dark Knight', year: 2008 },
  { title: '12 Angry Men', year: 1957 },
  { title: "Schindler's List", year: 1993 },
  { title: 'Pulp Fiction', year: 1994 },
  // 可补充更多选项
];

export default function AdaptiveTagsAutocomplete() {
  const [value, setValue] = useState([top100Films[13]]);
  const [visibleTagCount, setVisibleTagCount] = useState(Infinity);
  const [isFocused, setIsFocused] = useState(false);
  const inputContainerRef = useRef(null);

  // 计算可显示的标签数量
  const calculateVisibleTags = useCallback(() => {
    if (!inputContainerRef.current || value.length === 0) {
      setVisibleTagCount(Infinity);
      return;
    }

    const container = inputContainerRef.current;
    const containerWidth = container.offsetWidth;
    // 预留输入占位符的宽度(可根据实际样式调整)
    const reservedWidth = 80;
    let totalWidth = 0;
    let count = 0;

    // 创建临时元素模拟MUI Chip的样式,计算标签宽度
    const tempTag = document.createElement('span');
    tempTag.style.cssText = `
      display: inline-block;
      padding: 4px 10px;
      margin: 2px;
      border-radius: 16px;
      font-size: 14px;
      font-family: Roboto, Helvetica, Arial, sans-serif;
      background-color: #e0e0e0;
      white-space: nowrap;
    `;
    document.body.appendChild(tempTag);

    for (const option of value) {
      const label = option.title;
      tempTag.textContent = label;
      const tagWidth = tempTag.offsetWidth;
      // 累加标签宽度+margin,判断是否超出容器剩余空间
      if (totalWidth + tagWidth + 4 + reservedWidth <= containerWidth) {
        totalWidth += tagWidth + 4;
        count++;
      } else {
        break;
      }
    }

    document.body.removeChild(tempTag);
    setVisibleTagCount(count);
  }, [value]);

  // 聚焦时显示全部标签,失焦时计算可显示数量
  useEffect(() => {
    if (isFocused) {
      setVisibleTagCount(Infinity);
    } else {
      calculateVisibleTags();
    }
  }, [isFocused, calculateVisibleTags]);

  // 监听窗口大小变化,重新计算标签数量
  useEffect(() => {
    window.addEventListener('resize', calculateVisibleTags);
    return () => window.removeEventListener('resize', calculateVisibleTags);
  }, [calculateVisibleTags]);

  // 自定义标签渲染逻辑
  const renderTags = (renderValue, getTagProps) => {
    if (isFocused) {
      return renderValue.map((option, index) => (
        <Chip
          key={option.title}
          label={option.title}
          {...getTagProps({ index })}
        />
      ));
    }

    const visibleTags = renderValue.slice(0, visibleTagCount);
    const remaining = renderValue.length - visibleTagCount;

    return [
      ...visibleTags.map((option, index) => (
        <Chip
          key={option.title}
          label={option.title}
          {...getTagProps({ index })}
        />
      )),
      remaining > 0 && (
        <Chip
          key="remaining"
          label={`+${remaining}`}
          variant="outlined"
        />
      ),
    ];
  };

  return (
    <Autocomplete
      multiple
      id="tags-adaptive"
      options={top100Films}
      getOptionLabel={(option) => option.title}
      disableCloseOnSelect
      value={value}
      onChange={(event, newValue) => setValue(newValue)}
      renderTags={renderTags}
      renderInput={(params) => (
        <TextField
          {...params}
          variant="standard"
          label="Multiple values"
          placeholder="Favorites"
          onFocus={() => setIsFocused(true)}
          onBlur={() => setIsFocused(false)}
          InputProps={{
            ...params.InputProps,
            ref: inputContainerRef,
          }}
        />
      )}
    />
  );
}

关键细节说明

  • 标签宽度计算:通过临时元素模拟MUI Chip的真实样式,确保宽度计算准确,避免因样式差异导致的显示错误
  • 预留宽度:reservedWidth用于预留输入框占位符的空间,可根据实际占位符文本长度调整
  • 窗口监听:监听窗口resize事件,确保容器宽度变化时重新计算可显示标签数
  • 聚焦/失焦切换:聚焦时显示全部标签,保证用户操作时的体验;失焦时自动适配一行显示

内容的提问来源于stack exchange,提问作者Martin Kršek

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 11:00:09