Change Parent Upon Child Focus in React


Here's how to change a parent element's style when a child element is focused.

Parent Selector

It would be awesome if we could do this in CSS. Alas, there is no parent selector in CSS today. Maybe someday.

In CSS, we can style the element itself when it's focused:

.element:focus { color: 'thatfocusedcolor'; }

And we can style children when a parent is focused:

.some-parent:focus .some-child { color: 'thatfocusedcolor'; }

But we can't traverse upward in pure CSS. But we can accomplish this with some extra state and event handlers in JavaScript.

Capturing Focus in React

There are two easily-understood event handlers for knowing if something is focused in React: onFocus and onBlur (for unfocusing).

We attach them to the child element we're checking to see is focused or not:

<button className="some-child" 
     onFocus={someFunction}
     onBlur={someOtherFunction}>Some child!</button>

Now we need to implement the functions that will be called for the onFocus and onBlur events.

Styling a Parent in React

These functions will most effectively be implemented in the parent component. This is because it is the parent that needs to know whether the child is focused or not. So the parent will store some internal isFocused state:

class SomeParent extends React.Component {
  constructor(props) {
    super(props)
    this.state = { isFocused: false }
  }
  // ...
}

isFocused will start false, but it'll change as focus events fire on the child. When focused, it should switch to true and back to false on blur. Let's expand the parent code:

class SomeParent extends React.Component {
  constructor(props) {
    super(props)
    this.state = { isFocused: false }
  }
  render() {
    return (
      <div className={this.state.isFocused ? 'box focused' : 'box'}>
        {React.cloneElement(this.props.children, {
          onFocus: _ => this.setState({ isFocused: true }),
          onBlur: _ => this.setState({ isFocused: false })
        })}
      </div>
    ) 
  }
}

We are using React.cloneElement to allow the parent to set onFocus and onBlur props on the child without having to define them on the child outside of the SomeParent component. Usage of the component would look like:

<SomeParent>
  <button>Some child!</button>
</SomeParent>

Now we are left to implement the special .focused css selector that's being toggled on and off to see the style change. Try out this example on jsbin to see it in action. Click in the "Output" pane and press the tab key to change focus.

It might also be worth noting that the child must be focusable in order to fire the focus/blur events. In this case, we're using a natively-focusable element, button. But you can make other things focusable as well.

How do you control parent elements' style when child elements get focus?