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

xqkwcwgp  于 4个月前  发布在  React
关注(0)|答案(5)|浏览(79)

我已经创建了一个移动的菜单,根据状态切换打开和关闭。一旦它打开,我希望用户能够通过点击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,它跨越整个视口,然后只需要添加一个onClick even到那个div就可以改变状态,但是我读到了这个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>
        )
    }
}

kqhtkvqz

kqhtkvqz1#

尝试使用onMouseDown而不是onClick。

zzwlnbp8

zzwlnbp82#

关键是onBlur正在触发重新渲染,这似乎会导致浏览器不跟进onClick:https://github.com/facebook/react/issues/4210
但是如果你检查onBlur事件,你可以找到一些关于发生了什么的信息,event.relatedTarget被填充,你可以使用这些信息来检测onBlur何时被onClick触发,并链接你需要做的任何事情。

qlzsbp2j

qlzsbp2j3#

我刚刚在一个面包屑链接数组中遇到了这个问题,其中onBlur处理程序导致重新渲染,阻止链接点击工作。实际问题是react每次都会重新生成链接元素,所以当它重新渲染时,它会从鼠标下交换链接,这导致浏览器忽略点击。
修复方法是将key属性添加到我的链接中,以便react可以重用相同的DOM元素。

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

字符串

krcsximq

krcsximq4#

我也遇到了这个问题。对我有效的是这个弗兰肯斯坦式的解决方案:

onBlur={e => { 
    // regular onBlur code
    onBlurHandler(); // replace with your own onBlur event code handling

    // code to trigger button click event
    if(e.relatedTarget?.className.includes('save-button'){ // have to add a class to your save button like "save-button"
         const target = e.relatedTarget ? (e.relatedTarget as HTMLButtonElement): null; // can omit 'as HTMLButtonElement' if not in typescript
         target?.click(); // can omit the '?' if not in typescript
    }
}}

字符串
相当丑陋,但它的工作!希望它有助于其他一些可怜的灵魂困惑这一点。

i34xakig

i34xakig5#

我也有类似的问题,这里的建议都不太合适,所以我最终使用了setTimeout来稍微延迟blur事件。
在我的例子中,我使用的是一个输入组件,它在右边有一个可点击的“提交”按钮。只有当父对象被悬停在其中,和/或输入本身接收到焦点时,输入和按钮才会呈现。点击按钮会导致输入的onBlur触发,从而停止点击处理程序的运行。添加一个小的setTimeout解决了这个问题。

import { Fragment,useState } from "react";
import { FaArrowCircleRight, } from "react-icons/fa";
import {
    InputGroup,
    InputRightElement,
    Input,
    Box,
} from "@chakra-ui/react";

const HoverInput = props => {
    const [st_hovering,sst_hovering] = useState(false);
    const [st_focused,sst_focused] = useState(false);
    const [st_value,sst_value] = useState(props.savedValue || "");

    let handleChange = event=>{
        sst_value(event.target.value);
    };
    
    let handleSubmit = async (event)=>{
        event.preventDefault();
        // do stuff
    }; // handleSubmit

    return (
        <Box
            onMouseEnter={event=>sst_hovering(true)}
            onMouseLeave={event=>{
                if ( !st_focused ) {
                    sst_hovering(false);
                }
            }}
        >
            {
                !st_hovering ? (
                    <Fragment>{st_value}</Fragment>
                ) : (
                    <Box>
                        <InputGroup>
                            <Input
                                value={st_value}
                                onChange={handleChange}
                                onFocus={event=>sst_focused(true)}
                                onBlur={event=>{
                                    setTimeout(()=>{
                                        sst_focused(false);
                                    }, 100);
                                }}
                            />
                            <InputRightElement>
                                <FaArrowCircleRight
                                    cursor="pointer"
                                    onClick={handleSubmit}
                                />
                            </InputRightElement>
                        </InputGroup>
                    </Box>
                )
            }
        </Box>
    );
}

export default HoverInput;

字符串

相关问题