Material UI Autocomplete点击子菜单按钮时避免Blur导致关闭
问题描述
我需要为Material UI Autocomplete的每个选项添加下拉菜单,但点击打开子菜单的按钮时,Autocomplete会因Blur事件立即关闭。已尝试添加event.stopPropagation()并为菜单和按钮设置唯一ID,但问题仍存在,onClose日志显示关闭原因为blur。
期望实现以下交互:
- 点击绿色按钮时,Autocomplete保持打开;
- 点击选项其他区域时,正常选择该选项并关闭Autocomplete;
- 点击Autocomplete外部时,正常关闭Autocomplete。
问题代码:
import "./styles.css"; import TextField from "@mui/material/TextField"; import Autocomplete from "@mui/material/Autocomplete"; import * as React from "react"; import Button from "@mui/material/Button"; import Menu from "@mui/material/Menu"; import MenuItem from "@mui/material/MenuItem"; const options = [ { key: "1", value: "Option One" }, { key: "2", value: "Option Two" } ]; function BasicMenu({ option }) { const [anchorEl, setAnchorEl] = React.useState(null); const open = Boolean(anchorEl); const handleClick = (event) => { event.stopPropagation(); setAnchorEl(event.currentTarget); }; return ( <div> <Button color="success" variant="contained" id={`basic-button-${option.key}`} aria-controls={open ? `basic-menu-${option.key}` : undefined} aria-haspopup="true" aria-expanded={open ? "true" : undefined} onClick={handleClick} > open {option.value} menu </Button> <Menu id={`basic-menu-${option.key}`} anchorEl={anchorEl} open={open} MenuListProps={{ "aria-labelledby": `basic-button-${option.key}` }} > <MenuItem>Do with {option.value}</MenuItem> </Menu> </div> ); } export default function App() { return ( <div className="App"> <Autocomplete onClose={(e, reason) => { console.log(reason); }} getOptionLabel={({ value }) => value} renderOption={(props, value) => ( <li {...props}> {value.value} <BasicMenu option={value} /> </li> )} disablePortal id="combo-box-demo" options={options} renderInput={(params) => <TextField {...params} label="Option" />} /> </div> ); }
解决方案
核心问题是点击按钮时,焦点从Autocomplete移开触发了blur关闭逻辑。通过以下两点修改即可解决:
- 阻止按钮的mousedown事件冒泡:Autocomplete的关闭逻辑会监听mousedown事件,仅阻止click事件不够,需在按钮的
onMouseDown中调用event.preventDefault()和event.stopPropagation(),避免焦点转移触发blur。 - 手动控制Autocomplete的打开状态:通过状态变量在子菜单打开时强制保持Autocomplete为打开状态,子菜单关闭后恢复正常逻辑。
修改后的完整代码:
import "./styles.css"; import TextField from "@mui/material/TextField"; import Autocomplete from "@mui/material/Autocomplete"; import * as React from "react"; import Button from "@mui/material/Button"; import Menu from "@mui/material/Menu"; import MenuItem from "@mui/material/MenuItem"; const options = [ { key: "1", value: "Option One" }, { key: "2", value: "Option Two" } ]; function BasicMenu({ option, keepAutocompleteOpen }) { const [anchorEl, setAnchorEl] = React.useState(null); const open = Boolean(anchorEl); const handleClick = (event) => { event.stopPropagation(); setAnchorEl(event.currentTarget); keepAutocompleteOpen(true); }; const handleMenuClose = () => { setAnchorEl(null); keepAutocompleteOpen(false); }; const handleMouseDown = (event) => { event.preventDefault(); event.stopPropagation(); }; return ( <div> <Button color="success" variant="contained" id={`basic-button-${option.key}`} aria-controls={open ? `basic-menu-${option.key}` : undefined} aria-haspopup="true" aria-expanded={open ? "true" : undefined} onClick={handleClick} onMouseDown={handleMouseDown} > open {option.value} menu </Button> <Menu id={`basic-menu-${option.key}`} anchorEl={anchorEl} open={open} onClose={handleMenuClose} MenuListProps={{ "aria-labelledby": `basic-button-${option.key}` }} > <MenuItem onClick={handleMenuClose}>Do with {option.value}</MenuItem> </Menu> </div> ); } export default function App() { const [autocompleteOpen, setAutocompleteOpen] = React.useState(false); const [forceOpen, setForceOpen] = React.useState(false); const handleAutocompleteOpen = () => { setAutocompleteOpen(true); }; const handleAutocompleteClose = (e, reason) => { if (!forceOpen) { setAutocompleteOpen(false); } }; return ( <div className="App"> <Autocomplete open={autocompleteOpen} onOpen={handleAutocompleteOpen} onClose={handleAutocompleteClose} getOptionLabel={({ value }) => value} renderOption={(props, value) => ( <li {...props}> {value.value} <BasicMenu option={value} keepAutocompleteOpen={setForceOpen} /> </li> )} disablePortal id="combo-box-demo" options={options} renderInput={(params) => <TextField {...params} label="Option" />} /> </div> ); }
关键修改说明
- 给按钮新增
onMouseDown事件处理,阻止事件传播和默认行为,避免触发Autocomplete的blur关闭逻辑; - 新增
forceOpen状态变量,子菜单打开时设为true,强制Autocomplete保持打开; - 修改Autocomplete的
onClose逻辑,仅在forceOpen为false时执行关闭操作。
内容的提问来源于stack exchange,提问作者pomipiwimo
相关产品推荐
相关产品推荐

