简体   繁体   English

React - 带有 onBlur 事件的 ul 会阻止 onClick 在 li 上触发

[英]React - ul with onBlur event is preventing onClick from firing on li

I've created a mobile dropdown menu that toggles open and closed based on state.我创建了一个移动下拉菜单,可以根据状态切换打开和关闭。 Once it's open, I would like the user to be able to close the dropdown by clicking anywhere outside the ul.一旦它打开,我希望用户能够通过单击 ul 之外的任何地方来关闭下拉列表。

I'm setting the tabIndex attribute on the ul to 0, which gives the ul "focus".我将 ul 上的 tabIndex 属性设置为 0,这使 ul 成为“焦点”。 I've also added an onBlur event to the ul that triggers the state change (dropdownExpanded = false) that hides the ul.我还向 ul 添加了一个 onBlur 事件,该事件触发隐藏 ul 的状态更改 (dropdownExpanded = false)。

<ul tabIndex="0" onBlur={this.hideDropdownMenu}>
  <li onClick={this.handlePageNavigation}>Page 1</li>
  <li onClick={this.handlePageNavigation}>Page 2</li>
  <li onClick={this.handlePageNavigation}>Page 3</li>
</ul>

However, when I implement this fix, the onClick events that I have on each li element fail to fire.但是,当我实施此修复程序时,我在每个 li 元素上的 onClick 事件无法触发。

I know something is going on with the event bubbling, but I am at a lose as to how to fix it.我知道事件冒泡发生了一些事情,但我不知道如何解决它。 Can anyone help?任何人都可以帮忙吗?

NOTE:笔记:

I know you can create a transparent div below the ul that spans the entire viewport and then just add an onClick even to that div that will change the state, but I read about this tabIndex/focus solution on Stack Overflow and I'd really like to get it working.我知道你可以在 ul 下方创建一个透明的 div 来跨越整个视口,然后甚至向那个 div 添加一个 onClick 来改变状态,但是我在 Stack Overflow 上阅读了这个 tabIndex/focus 解决方案,我真的很喜欢让它工作。

Here is a more complete view of the code (the dropdown is for users to select their home country, which updates the ui):这是代码的更完整视图(下拉列表供用户选择他们的祖国,从而更新 ui):

const mapStateToProps = (state) => {
    return {
        lang: state.lang
    }
}

const mapDispatchToProps = (dispatch) => {
    return { actions: bindActionCreators({ changeLang }, dispatch) };
}

class Header extends Component {
    constructor() {
        super();

        this.state = {
          langListExpanded: false
        }

        this.handleLangChange = this.handleLangChange.bind(this);
        this.toggleLangMenu = this.toggleLangMenu.bind(this);
        this.hideLangMenu = this.hideLangMenu.bind(this);

    }

    toggleLangMenu (){
      this.setState({
        langListExpanded: !this.state.langListExpanded
      });
    }

    hideLangMenu (){
      this.setState({
        langListExpanded: false
      });
    }


    handleLangChange(e) {
        let newLang = e.target.attributes['0'].value;
        let urlSegment = window.location.pathname.substr(7);

        // blast it to shared state
        this.props.actions.changeLang( newLang );
        // update browser route to change locale, but stay where they are at
        browserHistory.push(`/${ newLang }/${ urlSegment }`);
        //close dropdown menu
        this.hideLangMenu();
    }

    compileAvailableLocales() {
        let locales = availableLangs;
        let selectedLang = this.props.lang;

        let markup = _.map(locales, (loc) => {
            let readableName = language[ selectedLang ].navigation.locales[ loc ];

            return (
                <li
                  key={ loc }
                  value={ loc }
                  onMouseDown={ this.handleLangChange }>
                    { readableName }
                </li>
            );
        });

        return markup;
    }


    render() {
        let localeMarkup = this.compileAvailableLocales();
        return (
            <section className="header row expanded">
              < Navigation />
            <section className="locale_selection">
                  <button
                    className="btn-locale"
                    onClick={this.toggleLangMenu}>
                    {this.props.lang}
                  </button>
                  <ul
                      className={this.state.langListExpanded ? "mobile_open" : " "}
                      value={ this.props.lang }
                      tabIndex="0"
                      onBlur={this.hideLangMenu}>
                  >
                      { localeMarkup }
                  </ul>

                </section>
            </section>
        )
    }
}

尝试使用 onMouseDown 而不是 onClick。

The point is the onBlur is triggering a re-render which seems to lead the browser to do not follow up with the onClick: https://github.com/facebook/react/issues/4210关键是 onBlur 正在触发重新渲染,这似乎导致浏览器不跟进 onClick: https : //github.com/facebook/react/issues/4210

But if you check the onBlur event you can find some info about what's happening, event.relatedTarget is populated and you can use these info to detect when the onBlur is actually triggered by the onClick and chain whatever you need to do.但是如果你检查 onBlur 事件,你可以找到一些关于正在发生的事情的信息, event.relatedTarget 被填充,你可以使用这些信息来检测 onBlur 何时实际被 onClick 触发并链接你需要做的任何事情。

I just ran into this with an array of breadcrumb links, where an onBlur handler was causing a rerender, preventing the link click from working.我刚刚用一系列面包屑链接遇到了这个问题,其中一个 onBlur 处理程序导致重新渲染,阻止链接点击工作。 The actual problem was that react was regenerating the link elements every time, so when it rerendered, it swapped the link out from under the mouse, which caused the browser to ignore the click.实际的问题是,react 每次都重新生成链接元素,所以当它重新渲染时,它会将链接从鼠标下方换出,这导致浏览器忽略了点击。

The fix was to add key properties to my links, so that react would reuse the same DOM elements.解决方法是向我的链接添加key属性,以便 react 可以重用相同的 DOM 元素。

<ol>
    {props.breadcrumbs.map(crumb => (
        <li key={crumb.url}>
            <Link to={crumb.url} >
                {crumb.label}
            </Link>
        </li>
    ))}
</ol>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM