Search code examples
reactjs

How can I prevent onBlur from executing when a button is clicked outside of a block with an onBlur event?


I have my own search input. When I enter a value into it, I have items matching the search terms displayed. I need to show these elements only when the input is in focus, and hide them when blurred. But here's the problem: when I click onClick on one of the items, I need to perform some actions but I don't want onBlur to be triggered. But when I click on an element, onBlur is triggered, which causes the elements to be hidden and the onClick event for the element is not triggered.

I tried using event.stopPropagation() and event.nativeEvent.stopImmediatePropagation(), but that didn't help. For some reason onBlur always triggers first even when the components order is changed.

<Container className='search-widget'>
            <div className={field-wrapper}>
                <SearchField
                    onFocus={() => {...}}
                    onBlur={() => {
                        console.log('onBlur')
                    }}
                />
                <Button onClick={...}>
                    <img src='/images/Search/search.svg' alt='search' />
                </Button>
            </div>
            <SearchList
                itemOnClick={data => {
                    console.log('onClick')
                }}
            />
        </Container>

when input onFocus

When onClick to element. onBlur is triggered and the menu is hidden


Solution

  • You need to use relatedTarget

    One way could be to use a ref like below. NOTE: this answer assumes that SearchList only contains clickable DOM items. If it renders things that are more than just clickable items, then it won't work if you click on something that is inside SearchList that isn't an item.

    Ultimately, relatedTarget is what you need. There's a myriad of ways that might work for you in how you choose to use it.

    const searchListWrapper = useRef()
    
    
    return (
              <Container className='search-widget'>
                <div className={field-wrapper}>
                    <SearchField
                        onFocus={() => {...}}
                        onBlur={(e) => {
                            const didClickItem = searchListWrapper.current.contains(e.relatedTarget);
    
                            if(!didClickItem) {
                               // user clicked something other than an item, do stuff
                            }
                        }}
                    />
                    <Button onClick={...}>
                        <img src='/images/Search/search.svg' alt='search' />
                    </Button>
                </div>
                <div ref={searchListWrapper}>
                 <SearchList
                     itemOnClick={data => {
                         console.log('onClick')
                     }}
                 />
                </div>
            </Container>
    );