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

Posted

技术标签:

【中文标题】React - 带有 onBlur 事件的 ul 阻止 onClick 在 li 上触发【英文标题】:React - ul with onBlur event is preventing onClick from firing on li 【发布时间】:2017-10-23 20:20:00 【问题描述】:

我创建了一个移动下拉菜单,可以根据状态切换打开和关闭。打开后,我希望用户能够通过单击 ul 之外的任何位置来关闭下拉菜单。

我将 ul 上的 tabIndex 属性设置为 0,这使 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>

但是,当我实施此修复时,我在每个 li 元素上的 onClick 事件都无法触发。

我知道事件冒泡发生了一些事情,但我不知道如何解决它。有人可以帮忙吗?

注意:

我知道您可以在 ul 下方创建一个跨越整个视口的透明 div,然后只需向该 div 添加一个 onClick 即可更改状态,但我阅读了有关此 tabIndex/focus 解决方案 on Stack Overflow 和我我真的很想让它工作。

这里是更完整的代码视图(下拉列表供用户选择他们的国家,这会更新 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>
        )
    

【问题讨论】:

【参考方案1】:

我刚刚遇到了一系列面包屑链接,其中一个 onBlur 处理程序导致重新呈现,从而阻止链接点击工作。实际问题是 react 每次都在重新生成链接元素,所以当它重新渲染时,它会将链接从鼠标下方换出,导致浏览器忽略点击。

解决方法是在我的链接中添加 key 属性,以便 react 重用相同的 DOM 元素。

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

【讨论】:

【参考方案2】:

关键是 onBlur 正在触发重新渲染,这似乎导致浏览器不跟进 onClick:https://github.com/facebook/react/issues/4210

但是,如果您检查 onBlur 事件,您可以找到一些关于正在发生的事情的信息,event.relatedTarget 已填充,您可以使用这些信息来检测 onBlur 何时实际由 onClick 触发并链接您需要执行的任何操作。

【讨论】:

使用 event.relatedTarget 对于不需要 onClick 处理程序的交互式元素(例如链接)特别有用。 嘿,你能看看这个吗? ***.com/questions/63686646/… event.relatedTarget onBlur 事件仅在新事物获得关注时才会填充。这要求列表元素是可聚焦的,这可能合适也可能不合适。 developer.mozilla.org/en-US/docs/Web/API/FocusEvent/…【参考方案3】:

尝试使用 onMouseDown 而不是 onClick。

【讨论】:

谢谢,斯蒂芬。我试过了。这确实使 li 上的事件着火了。但是现在当你点击离开 ul 时,onBlur 事件不会触发。 你能再展示一些你的代码吗?我在this jsfiddle 中对其进行了测试,虽然没有显示和隐藏菜单的代码,但似乎添加 onMouseDown 处理程序不会直接干扰 onBlur 处理程序。 感谢您的链接,斯蒂芬。我已经为整个组件添加了代码。 您的下拉菜单可能永远不会获得焦点,因此永远不会失去焦点,这会触发回调。我相信this fiddle 会做你想做的事。重要的变化在第 18 行和第 67 行。 是的!这似乎是正在发生的事情。你的解决方案奏效了。再次感谢斯蒂芬!

以上是关于React - 带有 onBlur 事件的 ul 阻止 onClick 在 li 上触发的主要内容,如果未能解决你的问题,请参考以下文章

react js中的window.onblur事件没有在android webview中被调用

反应和模糊事件

JS事件 失焦事件(onblur)onblur事件与onfocus是相对事件,当光标离开当前获得聚焦对象的时候,触发onblur事件,同时执行被调用的程序。

onblur 事件

React Native:onBlur 在 onPress 之后触发

在 JSX 和 React 中使用 onBlur