-1

If I've a simple event handler that's called when an input loses focus. Is there any way to tell from that event object what it lost focus to? ie, what the next focused element is?

I'm basically trying to have the search input perform one behaviour if it loses focus, but not if it loses focus to the search button that is next to the search input (as that's handled by a different listener).

So:

  1. submit form / click search btn => behaviour a
  2. click out of the input => behaviour b
  3. click out of the input onto the search btn => behaviour a

The code below would be where I would try and distinguish event 2 from 3

    handleFocusEvent(e) {
        const searchContainer = document.querySelector('.summary__item');
        const searchInput = e.target.closest('.summary__search-input');
        const nextFocusedElement = // Some property of the event object

        // So I'd like this to run if the input loses focus, but the nextFocusedElement is not a specific element
        if(searchInput && !nextFocusedElement) {
            searchInput.value = "";
            searchInput.animation.reverse();
        }
    }

*Edit Using 'onfocus' and 'relatedTarget' was recommended here for the problem I was getting when trying a suggested solution of activeTarget. But now instead of getting the body element, I'm just getting null

NickW
  • 1,207
  • 1
  • 12
  • 52
  • 1
    But why do you need to know this? – kelsny Aug 19 '22 at 14:00
  • 1
    There is an onfocusout event, might be worth looking into. – Phaelax z Aug 19 '22 at 14:03
  • @kelly If the searchbar loses focus it should do one behviour, but if it loses focus to the search button I'd like it to not do that behaviour as it's handled by a click event listener – NickW Aug 19 '22 at 14:03
  • But then couldn't you just check if *those* elements get focused, instead of trying to do "the other way around" and determining what element it lost focus to? – kelsny Aug 19 '22 at 14:04
  • Sure, but then wouldn't both the `blur` logic and the `click` logic run, as it's lost focus to the submit button? – NickW Aug 19 '22 at 14:05
  • 1
    [MDN document.activeElement](https://developer.mozilla.org/en-US/docs/Web/API/Document/activeElement) – Thomas Aug 19 '22 at 14:07
  • @Phaelaxz hmm, and then handle both in a parent? – NickW Aug 19 '22 at 14:07
  • @Thomas - that sounds perfect, but it seems to just be returning the `body` element for me. That definitely looks like the right approach though, thanks – NickW Aug 19 '22 at 14:11
  • This looks promising (focusout + relatedTarget): https://stackoverflow.com/a/67417491/7396516. Still not quite working though – NickW Aug 19 '22 at 14:17
  • 1
    This is not a full solution but [this post](https://stackoverflow.com/questions/42764494/blur-event-relatedtarget-returns-null) does offer some help/insight on your 'null' `relatedTarget`. Adding a `tabindex` of -1 to all non focus-able elements might not be viable, but it would resolve your current issue. – EssXTee Aug 19 '22 at 14:39
  • @EssXTee Thanks - was looking at something similar too. Was actually the reverse - my "button" wasn't a button, so adding `tabindex="0"` just to that element got `e.relatedTarget()` working with the `focusout` event. Thanks! – NickW Aug 19 '22 at 14:46
  • 1
    We're bordering on an XY problem here. Describe the problem, and the desired, expected and actual behaviour, but don't presume the technique that will be used to solve the problem. To be able to _"Find the element that another lost focus to"_ may be inconsequential to solving your actual problem which can likely be solved a different way. – Wyck Aug 19 '22 at 14:56
  • Hi @Wyck, if the input loses focus I'd like it to clear the input and the related state value (called `searchTerm`), and animate the input out. But if it loses focus to the search button I'd like it to execute the same function that submitting the form by pressing enter executes. That also leaves the `searchTerm` in place, adds a search tag, and animates the search input out. – NickW Aug 19 '22 at 15:28
  • I wouldn't be surprised if there's some underlying problem to how I'm structuring this, but it does seem like I need to be able to test for when the input loses focus, but not when it loses focus to that element – NickW Aug 19 '22 at 15:29

1 Answers1

0

The code needed a focusout rather than blur event, as suggested in the comments, then the relatedTarget event property could be used

In order to make it return the element, it needed to be focusable by adding the tabindex="0" attribute to the item

handleFocusEvent(e) { // 'focusout' event, not 'blur'
    const searchContainer = document.querySelector('.summary__item');
    const searchInput = e.target.closest('.summary__search-input');
    const elementToAvoid = document.querySelector('.bad-element')
    

    if(searchInput && e.relatedTarget !== elementToAvoid) {
        searchInput.value = "";
        searchInput.animation.reverse();
    }
}
NickW
  • 1,207
  • 1
  • 12
  • 52
  • 1
    It should be noted that `tabindex="0"` will allow that element to be focused via the **Tab** key, which may not be desirable in all situations (such as on the `body` element or a `div`). So you may want to use `tabindex="-1"` instead, which will allow the element to return a value for `relatedTarget` but not be focused via the **Tab** key, depending on which elements you want to track. – EssXTee Aug 19 '22 at 14:59