React MUI Autocomplete多选时如何根据空间自动限制显示标签数量?
实现MUI Autocomplete自适应一行的标签显示(多选)
可以实现这个需求,核心思路是动态计算失焦状态下输入容器可容纳的标签数量,替代固定的limitTags。以下是具体实现方案:
实现步骤
- 监听组件的聚焦/失焦状态,仅在失焦时计算可显示标签数
- 获取Autocomplete输入容器的宽度,预留输入占位符的空间
- 通过临时元素模拟标签样式,计算每个选中标签的实际宽度,累加判断可容纳数量
- 自定义
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
相关产品推荐
相关产品推荐

