Material UI 的 ClickAwayListener 在点击自身时如何关闭?

12
我有一个显示侧边栏开关,它显示在弹出框内部。所以理想情况下,如果你点击其他地方(弹出框元素外),弹出框应该消失。如果你在弹出框内点击,它就不应该消失。但是当我点击开关或显示侧边栏文本时,弹出框会消失。我使用 <div> 包裹了弹出框,但这并没有帮助解决问题。
Popper https://material-ui.com/api/popper/ Switch https://material-ui.com/api/switch/ ClickAwayListener https://material-ui.com/utils/click-away-listener/ 以下是 Popper 代码。
<ClickAwayListener onClickAway={this.handleClickAway}>
      <div>
        <Popper className={classes.popper} id={id} open={open} placement="bottom-end" anchorEl={anchorEl} transition>
          {({ TransitionProps }) => (
            <Fade {...TransitionProps} timeout={350}>
              <Paper className={classes.SliderBox}>
                <Switch
                  checked={this.state.checkedB}
                  onChange={this.handleChange('checkedB')}
                  value="checkedB"
                  color="primary"
                  onClick={handleDrawer}
                  className={classNames(classes.menuButton, sidebar && classes.hide)}
                />
                Display Sidebar
              </Paper>
            </Fade>
          )}
        </Popper>
        </div>
       </ClickAwayListener>

我这里有样本(虽然我无法使其工作,我不知道为什么在点击时它会出错)https://codesandbox.io/s/8pkm3x1902

在此输入图片描述


如果您能提供一个可重现此问题的 CodeSandbox,那将非常有帮助。我怀疑您应该直接将 ClickAwayListener 放在 Paper 的周围(在 Fade 内部),但是没有简单的方法来尝试这个解决方案,所以我不确定这是否是正确的解决方案。 - Ryan Cogswell
好的,这是代码,但我不知道为什么当我点击时它会在clickawaylistener上出现错误。https://codesandbox.io/s/8pkm3x1902 - Extelliqent
你没有定义handleClickAway()。能否添加一下?@Extelliqent - Shivam Gupta
已修复,谢谢,这是链接:https://codesandbox.io/s/8pkm3x1902 - Extelliqent
问题沙盒对我有效吗? - cyrf
2个回答

24

默认情况下,Popper 利用 portal (https://github.com/mui-org/material-ui/blob/v4.11.0/packages/material-ui/src/Popper/Popper.js#L202) 在 DOM 的另一个部分(作为 <body> 元素的直接子元素)渲染其内容,与 Popper 元素所在的位置不同。这意味着您需要将 ClickAwayListener 包装在一个空的 <div> 中,这样任何地方的点击(包括弹出窗口内部)都会被视为超出该空的 <div> 范围。

ClickAwayListener 直接包裹在 Fade 周围(而不是包裹在 Popper 周围)可以确保它围绕 Popper 实际呈现的内容。

以下是基于您的沙盒的示例:

import React, { Component } from "react";
import ReactDOM from "react-dom";
import Popper from "@material-ui/core/Popper";
import Fade from "@material-ui/core/Fade";
import Paper from "@material-ui/core/Paper";
import Switch from "@material-ui/core/Switch";
import Avatar from "@material-ui/core/Avatar";
import ClickAwayListener from "@material-ui/core/ClickAwayListener";

class App extends Component {
  state = {
    anchorEl: null,
    open: false,
    checkedB: true
  };

  handleClick = (event) => {
    const { currentTarget } = event;
    this.setState((state) => ({
      anchorEl: currentTarget,
      open: !state.open
    }));
  };

  handleChange = (name) => (event) => {
    this.setState({
      [name]: event.target.checked
    });
  };

  handleClickAway = () => {
    this.setState({
      open: false
    });
  };

  render() {
    const { anchorEl, open } = this.state;
    const { handleDrawer } = this.props;
    const id = open ? "simple-popper" : null;
    return (
      <div className="App">
        asdsadsa
        <Avatar
          alt="Test"
          src="https://www.nretnil.com/avatar/LawrenceEzekielAmos.png"
          style={{ margin: "0 10px" }}
          onClick={this.handleClick}
        />
        <div>
          <Popper
            id={id}
            open={open}
            placement="bottom-end"
            anchorEl={anchorEl}
            transition
          >
            {({ TransitionProps }) => (
              <ClickAwayListener onClickAway={this.handleClickAway}>
                <Fade {...TransitionProps} timeout={350}>
                  <Paper //className={classes.SliderBox}
                  >
                    <Switch
                      checked={this.state.checkedB}
                      onChange={this.handleChange("checkedB")}
                      value="checkedB"
                      color="primary"
                      onClick={handleDrawer}
                    />
                    Display Sidebar
                  </Paper>
                </Fade>
              </ClickAwayListener>
            )}
          </Popper>
        </div>
      </div>
    );
  }
}

export default App;

ReactDOM.render(<App />, document.getElementById("root"));

Edit Popper ClickAwayListener

在v4版本中,ClickAwayListener支持识别其React元素树内的元素,即使它们是在portal中呈现的(原问题针对v3),但仍然更可靠将ClickAwayListener放置在Popper内部而不是外部,以避免ClickAwayListener在单击打开Popper时触发,这会使其看起来像Popper没有工作,因为它立即关闭(示例:https://codesandbox.io/s/popper-clickawaylistener-forked-x4j0l?file=/src/index.js)。

2
好的,我把它移到了Fade之外,它完美地工作了,谢谢! - Extelliqent
@Ryan Cogswell 非常感谢!那个方法起作用了!这是一个 bug 吗?也许我们应该在 MUI 的 Github 存储库中开一个问题? - Manuel Abascal
1
@ManuelAbascal 不,这不是一个 bug,只是被错误使用了。 - Ryan Cogswell
@RyanCogswell,我不理解你所做的更改。看起来OP将ClickAwayListener作为所有内容的父级,但你将其作为子级。参考文献中的示例将其用作父级。你能详细说明一下你的解决方案吗? - cyrf
@RyanCogswell,我在页面上有一个相同的弹出框循环,这种情况下,点击弹出框会自动关闭。我们该如何处理? - Rajitha Kothapally
显示剩余3条评论

0
对我来说,仅仅将popupOpen=false设置为false并不能解决问题。因此,我还需要将anchorElement也设置为null,像这样:
const handleClickAway= ()=>{
popupOpen=false;   // Here you can also use state hook, but in my case, I'm using simpe boolean variable.
setAnchorEl(null);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接