facebook / react

The library for web and native user interfaces.
https://react.dev
MIT License
228.51k stars 46.76k forks source link

Add 'onClickoutside' event #579

Closed hojberg closed 9 years ago

hojberg commented 10 years ago

Add the 'clickoutside' event similar to this jquery plugin: https://github.com/cowboy/jquery-outside-events

Very useful sugar for components like dropdowns and modals that need to close/hide when you click out side the container.

yiminghe commented 9 years ago

need a way to bind events to body/document in react way, because react will batch setState in event handler, it is very useful.

such as:

React.createClass({
 onDocumentEventType:function(){
 }
})
sophiebits commented 9 years ago

You can use React.addons.batchedUpdates to get the same functionality.

yiminghe commented 9 years ago

@spicyj

It's not in docs? http://facebook.github.io/react/docs/addons.html

Better make it into core, we usually just use react core.

sophiebits commented 9 years ago

Ah, yes. #3570

sophiebits commented 9 years ago

As for this issue generally, see #1608. We're not planning to add this event. For many cases with popups and modals, you can add a backdrop element (either transparent or not) to capture clicks. For others, you can add a top-level listener to the document and catch clicks. #285 tracks adding a more complete solution to that, though for now manually calling addEventListener for that case is probably best.

reverofevil commented 8 years ago

Could we at least document here that React events are handled after all the native DOM events are handled?

sophiebits commented 8 years ago

@polkovnikov-ph That's not necessarily true – it depends when the other listeners are added.

dousaitis commented 7 years ago

Following @gaearon and @willdady suggestions a very simple DropDown implementation could be like the following one:

drop_down

Note that the DropDown remains open when user clicks on input field which is outside the DropDown.

DropDown.js

import React, { Component, PropTypes } from 'react';

class DropDown extends Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }

  componentDidMount() {
    document.addEventListener('click', this.props.onBodyClick);
  }

  componentWillUnmount() {
    document.removeEventListener('click', this.props.onBodyClick);
  }

  handleClick(e) {
    e.nativeEvent.stopImmediatePropagation();
  }

  render() {
    return (
      <div onClick={this.handleClick}>
        {this.props.children}
      </div>
    );
  }
}

DropDown.propTypes = {
  children: PropTypes.node,
  onBodyClick: PropTypes.func
};

DropDown.defaultProps = {
  onBodyClick: () => {}
};

export default DropDown;

MyComponent.js

import React, { Component, PropTypes } from 'react';
import cx from 'classnames';
import DropDown from './DropDown';

class MyComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isMenuOpen: false
    };
    this.handleInputClick = this.handleInputClick.bind(this);
    this.handleBodyClick = this.handleBodyClick.bind(this);
  }

  handleInputClick() {
    this.setState({ isMenuOpen: true });
  }

  handleBodyClick() {
    if (document.activeElement === this.textInput) {
      return;
    }
    this.setState({ isMenuOpen: false });
  }

  render() {
    return (
      <div>
        <input
          ref={(ref) => { this.textInput = ref; }}
          onClick={this.handleInputClick}
        />
        <DropDown onBodyClick={this.handleBodyClick}>
          <ul className={cx({ 'DropDown--hidden': !this.state.isMenuOpen })}>
            <li>Option 1</li>
            <li>Option 2</li>
            <li>Option 3</li>
          </ul>
        </DropDown>
      </div>
    );
  }
}

export default MyComponent;
Pomax commented 7 years ago

Also note that the react-onclickoutside library has seen tons of contributions from some 24 people over the last 8 months and has been on a constant modernization cycle, so as of this comment it's at v5.7, and implemented as Higher Order Component that can turn anything you throw at it into a component with onclickoutside monitoring, making dropdown menus really easy to implement.

jonmiles commented 7 years ago

@johndous I think this solution only works on desktop, what about mobile devices and touch events which do not trigger click events. Any suggestions on how to handle this?