JSFiddle - React, Tailwind, and code Playground

by IvanBender

HTML

<div id="root"></div>

CSS

div {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 300px;
  height: 100px;
  justify-content: space-between;
}

ul {
  position: absolute;
  top: 5px;
  left: 50px;
  width: 100px;
  height: 80px;
  background: #000;
  border-radius: 5px;
}

li {
  color: #fff;
}

Babel + JSX

class BlurExample extends React.Component {
  constructor(props) {
    super(props)

    this.state = { isOpen: false }
    this.timeOutId = null

    this.onClickHandler = this.onClickHandler.bind(this)
    this.onBlurHandler = this.onBlurHandler.bind(this)
    this.onFocusHandler = this.onFocusHandler.bind(this)
  }

  onClickHandler() {
    this.setState(currentState => ({
      isOpen: !currentState.isOpen
    }))
  }

  // Мы закрываем выпадающий список по таймеру setTimeout.
  // Это нужно чтобы для дочерних элементов событие выделения
  // происходило перед событием получения фокуса.
  onBlurHandler() {
    this.timeOutId = setTimeout(() => {
      this.setState({
        isOpen: false
      })
    })
  }

  // Не закрывать выпадающий список при получении фокуса дочерним элементом.
  onFocusHandler() {
    clearTimeout(this.timeOutId)
  }

  render() {
    // React assists us by bubbling the blur and
    // focus events to the parent.
    return (
      <>
      <div onBlur={this.onBlurHandler} onFocus={this.onFocusHandler}>
        <button onClick={this.onClickHandler} aria-haspopup="true" aria-expanded={this.state.isOpen}>
          Select an option
        </button>
        {this.state.isOpen && (
          <ul>
            <li>Option 1</li>
            <li>Option 2</li>
            <li>Option 3</li>
          </ul>
        )}
      </div>
      <button>Second</button>
      <button>third</button>
      </>
    )
  }
}

ReactDOM.render(
  <BlurExample />,
  document.getElementById('root')
);