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

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关闭逻辑。通过以下两点修改即可解决:

  1. 阻止按钮的mousedown事件冒泡:Autocomplete的关闭逻辑会监听mousedown事件,仅阻止click事件不够,需在按钮的onMouseDown中调用event.preventDefault()和event.stopPropagation(),避免焦点转移触发blur。
  2. 手动控制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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 13:43:13