0

I have a sidebar element that uses tabIndex and onBlur to control visibility, so when the user selects anything outside of the sidebar it automatically hides.

This works well, but I need to add a drop-down menu to the sidebar which then gets focus and causes the sidebar to collapse (before a user could select something).

state = {
  visible: true
}

componentDidMount () {
  this.focusSidebar()
}

componentDidUpdate () {
  if (this.state.visible) this.focusSidebar()
}

focusSidebar () {
  ReactDOM.findDOMNode(this.refs.sidebarRegion).focus()
}

hideSidebar () => {
  this.setState({ visible: false })
}

render () {
  return (
    <div
      onBlur={this.hideSidebar}
      tabIndex='0'
      className={`sidebar ${this.state.visible ? '' : 'hidden'}`}
      ref='sidebarRegion'
    >
      <select>
        <option>Foo</option>
      </select>
    </div>
  )
}

I'm not seeing a good way to handle this with my current implementation of the sidebar, but I'm trying to find a way to self-contain the sidebar element without needing to hoist the visible/hidden state outside of the component.

Fluidbyte
  • 5,162
  • 9
  • 47
  • 76

1 Answers1

2

You can use document.activeElement to achieve what you want. I will not add more details, as it was explained here. You can also take a look at this gist.

Here it is demonstrated with your code, I didn't add css, but a console log to tell you when it should hide:

class Hello extends React.Component {
 state = {
  visible: true
}

componentDidMount () {
  this.focusSidebar()
}

componentDidUpdate () {
  if (this.state.visible) this.focusSidebar()
}

focusSidebar () {
  ReactDOM.findDOMNode(this.refs.sidebarRegion).focus()
}

hideSidebar(e) {
  var currentTarget = e.currentTarget;
  setTimeout(()=>{
    if (!currentTarget.contains(document.activeElement)) {
      this.setState({ visible: false })
      console.log("Hiding the sidebar!");
    }
}, 0);
}

render () {
  return (
    <div
      onBlur={this.hideSidebar.bind(this)}
      tabIndex='0'
      className={`sidebar ${this.state.visible ? '' : 'hidden'}`}
      ref='sidebarRegion'
    >
      <select>
        <option>Foo</option>
      </select>
    </div>
  )
}
}

ReactDOM.render(
  <Hello name="World" />,
  document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container">
    <!-- This element's contents will be replaced with your component. -->
</div>
Ankari
  • 609
  • 5
  • 8