React 17 internally uses the browser's focusin and focusout events for onFocus and onBlur events


In React, the onFocus event is called when the element receives focus and onBlur event is called when focus has left the element. There are 4 types of native focus events, focus/blur which do not bubble and focusin/focusout which bubble.

Before React 17, the onFocus and onBlur events were internally mapped to focus and blur events in the capture phase, giving an impression as if the events bubbled.

This has lead to confusion and several problems related to relatedTarget.

In the world of Keyboard accessibility, relatedTarget plays an important role. IE 9-11 has native support for relatedTarget in focusin and focusout, but in React’s onBlur event, its value is null. This is because React checks for the availability of event capturing to use the blur/focus events. If capturing is not supported, it uses the focusin/focusout event (< IE 9). Starting IE 9, IE team has added support for addEventListener and trapping capturing events. This means that IE 8 and before, React will have relatedTarget on onFocus and onBlur handlers but in IE 9-11 it will be null.

To understand this impact, let’s check out the example of navigating a dropdown menu using a keyboard. When we try to navigate to the list using the tab, the menu item should be focussed and when we move out of the menu it the dropdown menu should be closed.

export default function App() {
  const [isOpen, setOpen] = useState(false);
  const handleClick = (event) => {
    setOpen(!isOpen);
  }
  const handleBlur = (event) => {
    console.log('event.relatedTarget', event.relatedTarget);
    if (!event.currentTarget.contains(event.relatedTarget)) {
      if (isOpen) {
        setOpen(false);
      }
    }
  }
  return (
    <div className="disclosure-button-links" onBlur={handleBlur}>
      <button type="button" aria-expanded={isOpen} aria-controls="id-container" onClick={handleClick} >
        Saeloun blogs
      </button>
      { isOpen &&
        <ul id="id-container" className="menu"  >
          <li>
            <a href="https://blog.saeloun.com/category/react/"> React </a>
          </li>
          <li>
            <a href="https://blog.saeloun.com/category/rails/">Rails</a>
          </li>
          <li>
            <a href="https://blog.saeloun.com/category/ruby/">Ruby</a>
          </li>
        </ul>
      }
    </div>
  );
}

Before

Before React 17, while trying to navigate through the list using the tab in IE 9-11, the dropdown menu gets closed. This is because relatedTarget is null.

After

With the changes in React 17, onFocus and onBlur are mapped to focusin and focusout instead of focus and blur events thereby making relatedTarget available in IE 9-11. These changes align React closer with the browser behavior and improve interoperability.

Note:

Due to the breaking changes, React will not support Firefox versions earlier than 52 as these versions do not support focusin and focusout.

Check out the pull request to know more.