最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - React Material UI open modal from within autocomplete lose focus - Stack Overflow

programmeradmin6浏览0评论

I'm using the material-ui lib and I need to have an autoplete where each item inside that autoplete is clickable and opens a modal.

The structure in general is:

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

    );
}

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

}

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

The issue is that when you click on the button inside the modal - the focus is kept inside the autoplete, and the modal will never gets the focus (if I have an input inside the modal - I can't write anything inside).

Tried all the standard autoplete/modal focus-related props (disableEnforceFocus, disableEnforceFocus, etc...) but nothing works.

Here is a working codesandbox example. As you can see - if you click on the button that is not inside the autoplete ponent - everything works (you can add text inside the input field). If you click on the button that is inside the autoplete - the input field inside the modal is not editable (you lose focus).

Here is an example of the issue:

I'm using the material-ui lib and I need to have an autoplete where each item inside that autoplete is clickable and opens a modal.

The structure in general is:

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

    );
}

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

}

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

The issue is that when you click on the button inside the modal - the focus is kept inside the autoplete, and the modal will never gets the focus (if I have an input inside the modal - I can't write anything inside).

Tried all the standard autoplete/modal focus-related props (disableEnforceFocus, disableEnforceFocus, etc...) but nothing works.

Here is a working codesandbox example. As you can see - if you click on the button that is not inside the autoplete ponent - everything works (you can add text inside the input field). If you click on the button that is inside the autoplete - the input field inside the modal is not editable (you lose focus).

Here is an example of the issue:

Share Improve this question edited Apr 23, 2020 at 22:37 Dekel asked Apr 7, 2020 at 8:42 DekelDekel 62.6k12 gold badges107 silver badges130 bronze badges 5
  • Can you please check the sandbox, it's not working – Sabbin Commented Apr 7, 2020 at 9:07
  • @Sabbin sorry about that, should work now. – Dekel Commented Apr 7, 2020 at 9:16
  • Yes it works, just have one question, do you need 2 separate modals with the same functionality? The question is that will you have more than one opened at the time? – Sabbin Commented Apr 7, 2020 at 9:22
  • Multiple modals should be supported, but the important thing is that I need the modal to be opened from within the autoplete (I assume that once this issue is resolved, the nested modals should be resolved as well). – Dekel Commented Apr 7, 2020 at 9:30
  • 1 @RyanCogswell it does :) thanks for this! I usually wait with the award of the bounty till last day to get more exposure on the answers. – Dekel Commented Apr 28, 2020 at 15:18
Add a ment  | 

2 Answers 2

Reset to default 10 +50

The problem with having the Modal rendered from within the Autoplete is that events propagate from the Modal to the Autoplete. In particular, click and mouse-down events are both handled by Autoplete in a manner that causes problems in your case. This is primarily logic intended to keep focus in the right place as you interact with different parts of the Autoplete.

Below (from https://github./mui-org/material-ui/blob/v4.9.11/packages/material-ui-lab/src/useAutoplete/useAutoplete.js#L842) is the portion of the Autoplete code that is getting in your way:

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

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

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

    firstFocus.current = false;
  };

The default browser behavior when a mouse down event occurs on a focusable element is for that element to receive focus, but the mouse-down handler for Autoplete calls event.preventDefault() which prevents this default behavior and thus prevents a focus change from the mouse-down event (so focus stays on the Modal itself as indicated by its blue focus outline). You can however successfully move focus to the Modal's TextField using the tab key, since nothing is preventing that mechanism of focus change.

The Autoplete click handler is keeping focus on the input of the Autoplete even if you click some other part of the Autoplete. When your Modal is open, the effect of this is that when you click in the Modal, focus is moved briefly to the Autoplete input element, but the focus is immediately returned to the Modal due to its "enforce focus" functionality. If you add the disableEnforceFocus property to the Modal, you'll see that when you click in the Modal (e.g. on the TextField) the cursor remains in the input of the Autoplete.

The fix is to make sure that these two events do NOT propagate beyond the Modal. By calling event.stopPropagation() for both the click and mouse-down events on the Modal, it prevents the Autoplete functionality for these two events from being executed when these events occur within the Modal.

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

Related answer: How can I create a clickable first option in Material UI Labs Autoplete

The problem in your code was that the Modal was rendered from within the tag of the AutoComplete ponent, which was not ok because of the visibility of the ponents, the hierarchy of the ponents was the issue.

The fix is to move the Modal within the FixedTags ponent and pass the open handler to the ModalBtn in the renderTags prop;

I've updated your sandbox with a working variant HERE

The changes are below

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 (
    <>
      <Autoplete
        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>
    </>
  );
};
发布评论

评论列表(0)

  1. 暂无评论