React Material UI 从自动完成中打开模式失去焦点

React Material UI open modal from within autocomplete lose focus

我正在使用 material-ui 库,我需要一个自动完成功能,其中自动完成功能中的每个项目都可以点击并打开一个模式。

大体的结构是:

const ModalBtn = () => {
    ...
    return (
        <>
            <button ... (on click set modal to open)
            <Modal ...
        </>

    );
}

const AutoCompleteWithBtns = () => {
    return (
        <Autocomplete
            renderTags={(value, getTagProps) =>
                value.map((option, index) => <ModalBtn />)
            }
            ...
        />
    );

}

Note that the ModalBtn is a component that cannot be divided into two components of Button and Modal.

问题是,当您单击模态内的按钮时 - 焦点保留在自动完成内,并且模态永远不会获得焦点(如果我在模态内有输入 - 我无法编写里面的任何东西)。

尝试了所有标准的 autocomplete/modal 焦点相关道具(disableEnforceFocusdisableEnforceFocus 等...),但没有任何效果。

这里是working codesandbox example。如您所见 - 如果您单击不在自动完成组件内的按钮 - 一切正常(您可以在输入字段内添加文本)。如果您单击自动完成内的按钮 - 模态内的输入字段不可编辑(您失去焦点)。

这是问题的一个例子:

您的代码中的问题是模态框是从 AutoComplete 组件的标记内呈现的,这不正常,因为组件的可见性、组件的层次结构是问题所在。

修复方法是将 Modal 移动到 FixedTags 组件中,并将打开的处理程序传递给 renderTags 属性中的 ModalBtn

我已经用一个工作变体更新了你的沙箱 HERE

变化如下

const ModalBtn = ({ handleOpen }) => (
  <button type="button" onClick={handleOpen}>
    Open Modal (not working)
  </button>
);

const FixedTags = function() {
  const classes = useStyles();
  const [modalStyle] = React.useState(getModalStyle);
  const [open, setOpen] = React.useState(false);

  const handleOpen = () => {
    setOpen(true);
  };

  const handleClose = () => {
    setOpen(false);
  };

  return (
    <>
      <Autocomplete
        multiple
        options={autoCompleteItems}
        getOptionLabel={option => option.title}
        defaultValue={[autoCompleteItems[1], autoCompleteItems[2]]}
        renderTags={(value, getTagProps) =>
          value.map((option, index) => <ModalBtn handleOpen={handleOpen} />)
        }
        style={{ width: 500 }}
        renderInput={params => (
          <TextField
            {...params}
            label="Fixed tag"
            variant="outlined"
            placeholder="items..."
          />
        )}
      />
      <Modal open={open} onClose={handleClose}>
        <div style={modalStyle} className={classes.paper}>
          <h2 style={{ color: "red" }}>This one doesn't work</h2>
          <p>Text field is not available</p>
          <TextField label="Filled" variant="filled" /> <br />
          <br />
          <br />
          <FixedTags label="Standard" />
        </div>
      </Modal>
    </>
  );
};

Autocomplete 中呈现 Modal 的问题是事件从 Modal 传播到 Autocomplete。特别是,单击和鼠标按下事件均由 Autocomplete 以导致您的情况出现问题的方式处理。这主要是为了在您与 Autocomplete.

的不同部分交互时将焦点保持在正确的位置

下面(来自 https://github.com/mui-org/material-ui/blob/v4.9.11/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js#L842)是 Autocomplete 代码中妨碍您的部分:

  // Prevent input blur when interacting with the combobox
  const handleMouseDown = (event) => {
    if (event.target.getAttribute('id') !== id) {
      event.preventDefault();
    }
  };

  // Focus the input when interacting with the combobox
  const handleClick = () => {
    inputRef.current.focus();

    if (
      selectOnFocus &&
      firstFocus.current &&
      inputRef.current.selectionEnd - inputRef.current.selectionStart === 0
    ) {
      inputRef.current.select();
    }

    firstFocus.current = false;
  };

在可聚焦元素上发生鼠标按下事件时,浏览器的默认行为是让该元素获得焦点,但 Autocomplete 的鼠标按下处理程序会调用 event.preventDefault() 来阻止此默认行为从而防止鼠标按下事件引起的焦点变化(因此焦点停留在 Modal 本身,如其蓝色焦点轮廓所示)。但是,您可以使用 Tab 键成功地将焦点移动到模态框的 TextField,因为没有什么可以阻止焦点更改机制。

即使您单击 Autocomplete 的其他部分,Autocomplete 单击处理程序仍将焦点放在 Autocomplete 的输入上。当您的 Modal 打开时,其效果是当您在 Modal 中单击时,焦点会短暂地移动到 Autocomplete 输入元素,但焦点会立即返回到 Modal 由于其 "enforce focus" 功能。如果您将 disableEnforceFocus 属性 添加到 Modalyou'll see,那么当您单击 Modal(例如在 TextField 上)时,光标将保留在输入中Autocomplete.

解决方法是确保这两个事件不会传播到Modal之外。通过为 Modal 上的单击和鼠标按下事件调用 event.stopPropagation(),它可以防止在 [=12= 中发生这两个事件时执行这两个事件的 Autocomplete 功能].

      <Modal
        onClick={event => event.stopPropagation()}
        onMouseDown={event => event.stopPropagation()}
        ...

相关回答: