No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

README.md 14 KiB

hace 3 años
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. [![npm version](https://img.shields.io/npm/v/react-onclickoutside.svg)](https://www.npmjs.com/package/react-onclickoutside)
  2. [![Build Status](https://travis-ci.org/Pomax/react-onclickoutside.svg?branch=master)](https://travis-ci.org/Pomax/react-onclickoutside)
  3. [![npm](https://img.shields.io/npm/dm/react-onclickoutside.svg)](https://www.npmjs.com/package/react-onclickoutside)
  4. # An onClickOutside wrapper for React components
  5. This is a React Higher Order Component (HOC) that you can use with your own
  6. React components if you want to have them listen for clicks that occur somewhere
  7. in the document, outside of the element itself (for instance, if you need to
  8. hide a menu when people click anywhere else on your page).
  9. Note that this HOC relies on the `.classList` property, which is supported by
  10. all modern browsers, but not by deprecated and obsolete browsers like IE (noting
  11. that Microsoft Edge is not Microsoft Internet Explorer. Edge does not have any
  12. problems with the `classList` property for SVG elements). If your code relies on
  13. classList in any way, you want to use a polyfill like
  14. [dom4](https://github.com/WebReflection/dom4).
  15. This HOC supports stateless components as of v5.7.0, and switched to using
  16. transpiled es6 classes rather than `createClass` as of v6.
  17. ## Sections covered in this README
  18. * [Installation](#installation)
  19. * [Usage:](#usage)
  20. * [ES6 Class Component](#es6-class-component)
  21. * [Functional Component with UseState Hook](#functional-component-with-usestate-hook)
  22. * [CommonJS Require](#commonjs-require)
  23. * [Ensuring there's a click handler](#ensuring-there-is-a-click-handler)
  24. * [Regulate which events to listen for](#regulate-which-events-to-listen-for)
  25. * [Regulate whether or not to listen for outside clicks](#regulate-whether-or-not-to-listen-for-outside-clicks)
  26. * [Regulate whether or not to listen to scrollbar clicks](#regulate-whether-or-not-to-listen-to-scrollbar-clicks)
  27. * [Regulating `evt.preventDefault()` and `evt.stopPropagation()`](#regulating-evtpreventdefault-and-evtstoppropagation)
  28. * [Marking elements as "skip over this one" during the event loop](#marking-elements-as-skip-over-this-one-during-the-event-loop)
  29. * [Older React code: "What happened to the Mixin??"](#older-react-code-what-happened-to-the-mixin)
  30. * [But how can I access my component? It has an API that I rely on!](#but-how-can-i-access-my-component-it-has-an-api-that-i-rely-on)
  31. * [Which version do I need for which version of React?](#which-version-do-i-need-for-which-version-of-react)
  32. * [Support-wise, only the latest version will receive updates and bug fixes.](#support-wise-only-the-latest-version-will-receive-updates-and-bug-fixes)
  33. * [IE does not support classList for SVG elements!](#ie-does-not-support-classlist-for-svg-elements)
  34. * [I can't find what I need in the README](#i-cant-find-what-i-need-in-the-readme)
  35. ## Installation
  36. Use `npm`:
  37. ```
  38. $> npm install react-onclickoutside --save
  39. ```
  40. (or `--save-dev` depending on your needs). You then use it in your components
  41. as:
  42. ## Usage
  43. ### ES6 Class Component
  44. ```js
  45. import React, { Component } from "react";
  46. import onClickOutside from "react-onclickoutside";
  47. class MyComponent extends Component {
  48. handleClickOutside = evt => {
  49. // ..handling code goes here...
  50. };
  51. }
  52. export default onClickOutside(MyComponent);
  53. ```
  54. ### Functional Component with UseState Hook
  55. ```js
  56. import React, { useState } from "react";
  57. import onClickOutside from "react-onclickoutside";
  58. const Menu = () => {
  59. const [isOpen, setIsOpen] = useState(false);
  60. const toggle = () => setIsOpen(!isOpen);
  61. Menu.handleClickOutside = () => setIsOpen(false);
  62. return (
  63. //...
  64. )
  65. };
  66. const clickOutsideConfig = {
  67. handleClickOutside: () => Menu.handleClickOutside
  68. };
  69. export default onClickOutside(Menu, clickOutsideConfig);
  70. ```
  71. Example: https://codesandbox.io/s/vn66kq7mml
  72. ### CommonJS Require
  73. ```js
  74. // .default is needed because library is bundled as ES6 module
  75. var onClickOutside = require("react-onclickoutside").default;
  76. var createReactClass = require("create-react-class");
  77. // create a new component, wrapped by this onclickoutside HOC:
  78. var MyComponent = onClickOutside(
  79. createReactClass({
  80. // ...,
  81. handleClickOutside: function(evt) {
  82. // ...handling code goes here...
  83. }
  84. // ...
  85. })
  86. );
  87. ```
  88. ### Ensuring there is a click handler
  89. Note that if you try to wrap a React component class without a
  90. `handleClickOutside(evt)` handler like this, the HOC will throw an error. In
  91. order to use a custom event handler, you can specify the function to be used by
  92. the HOC as second parameter (this can be useful in environments like TypeScript,
  93. where the fact that the wrapped component does not implement the handler can be
  94. flagged at compile-time):
  95. ```js
  96. // load the HOC:
  97. import React, { Component } from "react";
  98. import onClickOutside from "react-onclickoutside";
  99. // create a new component, wrapped below by onClickOutside HOC:
  100. class MyComponent extends Component {
  101. // ...
  102. myClickOutsideHandler(evt) {
  103. // ...handling code goes here...
  104. }
  105. // ...
  106. }
  107. var clickOutsideConfig = {
  108. handleClickOutside: function(instance) {
  109. return instance.myClickOutsideHandler;
  110. }
  111. };
  112. var EnhancedComponent = onClickOutside(MyComponent, clickOutsideConfig);
  113. ```
  114. Note that if you try to wrap a React component with a custom handler that the
  115. component does not implement, the HOC will throw an error at run-time.
  116. ## Regulate which events to listen for
  117. By default, "outside clicks" are based on both `mousedown` and `touchstart`
  118. events; if that is what you need, then you do not need to specify anything
  119. special. However, if you need different events, you can specify these using the
  120. `eventTypes` property. If you just need one event, you can pass in the event
  121. name as plain string:
  122. ```js
  123. <MyComponent eventTypes="click" ... />
  124. ```
  125. For multiple events, you can pass in the array of event names you need to listen
  126. for:
  127. ```js
  128. <MyComponent eventTypes={["click", "touchend"]} ... />
  129. ```
  130. ## Regulate whether or not to listen for outside clicks
  131. Wrapped components have two functions that can be used to explicitly listen for,
  132. or do nothing with, outside clicks
  133. * `enableOnClickOutside()` - Enables outside click listening by setting up the
  134. event listening bindings.
  135. * `disableOnClickOutside()` - Disables outside click listening by explicitly
  136. removing the event listening bindings.
  137. In addition, you can create a component that uses this HOC such that it has the
  138. code set up and ready to go, but not listening for outside click events until
  139. you explicitly issue its `enableOnClickOutside()`, by passing in a properly
  140. called `disableOnClickOutside`:
  141. ```js
  142. import React, { Component } from "react";
  143. import onClickOutside from "react-onclickoutside";
  144. class MyComponent extends Component {
  145. // ...
  146. handleClickOutside(evt) {
  147. // ...
  148. }
  149. // ...
  150. }
  151. var EnhancedComponent = onClickOutside(MyComponent);
  152. class Container extends Component {
  153. render(evt) {
  154. return <EnhancedComponent disableOnClickOutside={true} />;
  155. }
  156. }
  157. ```
  158. Using `disableOnClickOutside()` or `enableOnClickOutside()` within
  159. `componentDidMount` or `componentWillMount` is considered an anti-pattern, and
  160. does not have consistent behaviour when using the mixin and HOC/ES7 Decorator.
  161. Favour setting the `disableOnClickOutside` property on the component.
  162. ## Regulate whether or not to listen to scrollbar clicks
  163. By default this HOC will listen for "clicks inside the document", which may
  164. include clicks that occur on the scrollbar. Quite often clicking on the
  165. scrollbar _should_ close whatever is open but in case your project invalidates
  166. that assumption you can use the `excludeScrollbar` property to explicitly tell
  167. the HOC that clicks on the scrollbar should be ignored:
  168. ```js
  169. import React, { Component } from "react";
  170. import onClickOutside from "react-onclickoutside";
  171. class MyComponent extends Component {
  172. // ...
  173. }
  174. var EnhancedComponent = onClickOutside(MyComponent);
  175. class Container extends Component {
  176. render(evt) {
  177. return <EnhancedComponent excludeScrollbar={true} />;
  178. }
  179. }
  180. ```
  181. Alternatively, you can specify this behavior as default for all instances of
  182. your component passing a configuration object as second parameter:
  183. ```js
  184. import React, { Component } from "react";
  185. import onClickOutside from "react-onclickoutside";
  186. class MyComponent extends Component {
  187. // ...
  188. }
  189. var clickOutsideConfig = {
  190. excludeScrollbar: true
  191. };
  192. var EnhancedComponent = onClickOutside(MyComponent, clickOutsideConfig);
  193. ```
  194. ## Regulating `evt.preventDefault()` and `evt.stopPropagation()`
  195. Technically this HOC lets you pass in `preventDefault={true/false}` and
  196. `stopPropagation={true/false}` to regulate what happens to the event when it
  197. hits your `handleClickOutside(evt)` function, but beware: `stopPropagation` may
  198. not do what you expect it to do.
  199. Each component adds new event listeners to the document, which may or may not
  200. cause as many event triggers as there are event listening bindings. In the test
  201. file found in `./test/browser/index.html`, the coded uses
  202. `stopPropagation={true}` but sibling events still make it to "parents".
  203. ## Marking elements as "skip over this one" during the event loop
  204. If you want the HOC to ignore certain elements, you can tell the HOC which CSS
  205. class name it should use for this purposes. If you want explicit control over
  206. the class name, use `outsideClickIgnoreClass={some string}` as component
  207. property, or if you don't, the default string used is
  208. `ignore-react-onclickoutside`.
  209. ## Older React code: "What happened to the Mixin??"
  210. Due to ES2015/ES6 `class` syntax making mixins essentially impossible, and the
  211. fact that HOC wrapping works perfectly fine in ES5 and older versions of React,
  212. as of this package's version 5.0.0 no Mixin is offered anymore.
  213. If you _absolutely_ need a mixin... you really don't.
  214. ### But how can I access my component? It has an API that I rely on!
  215. No, I get that. I constantly have that problem myself, so while there is no
  216. universal agreement on how to do that, this HOC offers a `getInstance()`
  217. function that you can call for a reference to the component you wrapped, so that
  218. you can call its API without headaches:
  219. ```js
  220. import React, { Component } from 'react'
  221. import onClickOutside from 'react-onclickoutside'
  222. class MyComponent extends Component {
  223. // ...
  224. handleClickOutside(evt) {
  225. // ...
  226. }
  227. ...
  228. }
  229. var EnhancedComponent = onClickOutside(MyComponent);
  230. class Container extends Component {
  231. constructor(props) {
  232. super(props);
  233. this.getMyComponentRef = this.getMyComponentRef.bind(this);
  234. }
  235. someFunction() {
  236. var ref = this.myComponentRef;
  237. // 1) Get the wrapped component instance:
  238. var superTrueMyComponent = ref.getInstance();
  239. // and call instance functions defined for it:
  240. superTrueMyComponent.customFunction();
  241. }
  242. getMyComponentRef(ref) {
  243. this.myComponentRef = ref;
  244. }
  245. render(evt) {
  246. return <EnhancedComponent disableOnClickOutside={true} ref={this.getMyComponentRef}/>
  247. }
  248. }
  249. ```
  250. Note that there is also a `getClass()` function, to get the original Class that
  251. was passed into the HOC wrapper, but if you find yourself needing this you're
  252. probably doing something wrong: you really want to define your classes as real,
  253. require'able etc. units, and then write wrapped components separately, so that
  254. you can always access the original class's `statics` etc. properties without
  255. needing to extract them out of a HOC.
  256. ## Which version do I need for which version of React?
  257. If you use **React 0.12 or 0.13**, **version 2.4 and below** will work.
  258. If you use **React 0.14**, use **v2.5 through v4.9**, as these specifically use
  259. `react-DOM` for the necessary DOM event bindings.
  260. If you use **React 15**, you can use **v4.x, which offers both a mixin and HOC,
  261. or use v5.x, which is HOC-only**.
  262. If you use **React 15.5**, you can use **v5.11.x**, which relies on
  263. `createClass` as supplied by `create-react-class` rather than
  264. `React.createClass`.
  265. If you use **React 16** or 15.5 in preparation of 16, use v6.x, which uses pure
  266. class notation.
  267. ### Support-wise, only the latest version will receive updates and bug fixes.
  268. I do not believe in perpetual support for outdated libraries, so if you find one
  269. of the older versions is not playing nice with an even older React: you know
  270. what to do, and it's not "keep using that old version of React".
  271. ## IE does not support classList for SVG elements!
  272. This is true, but also an edge-case problem that only exists for IE11 (as all
  273. versions prior to 11 [no longer exist](https://support.microsoft.com/en-us/help/17454/lifecycle-faq-internet-explorer)), and should be addressed by you, rather
  274. than by thousands of individual libraries that assume browsers have proper
  275. HTML API implementations (IE Edge has proper `classList` support even for SVG).
  276. If you need this to work, you can add a shim for `classList` to your page(s),
  277. loaded before you load your React code, and you'll have instantly fixed _every_
  278. library that you might remotely rely on that makes use of the `classList`
  279. property. You can find several shims quite easily, a good one to start with is
  280. the [dom4](https://github.com/WebReflection/dom4) shim, which adds all manner of
  281. good DOM4 properties to "not quite at DOM4 yet" browser implementations.
  282. Eventually this problem will stop being one, but in the mean time _you_ are
  283. responsible for making _your_ site work by shimming everything that needs
  284. shimming for IE. As such, **if you file a PR to fix classList-and-SVG issues
  285. specifically for this library, your PR will be closed and I will politely point
  286. you to this README.md section**. I will not accept PRs to fix this issue. You
  287. already have the power to fix it, and I expect you to take responsibility as a
  288. fellow developer to shim what you need instead of getting obsolete quirks
  289. supported by libraries whose job isn't to support obsolete quirks.
  290. To work around the issue you can use this simple shim:
  291. ```js
  292. if (!("classList" in SVGElement.prototype)) {
  293. Object.defineProperty(SVGElement.prototype, "classList", {
  294. get() {
  295. return {
  296. contains: className => {
  297. return this.className.baseVal.split(" ").indexOf(className) !== -1;
  298. }
  299. };
  300. }
  301. });
  302. }
  303. ```
  304. ## I can't find what I need in the README
  305. If you've read the whole thing and you still can't find what you were looking
  306. for, then the README is missing important information that should be added in.
  307. Please [file an issue](https://github.com/Pomax/react-onclickoutside/issues) with a request for additional documentation,
  308. describing what you were hoping to find in enough detail that it can be used to
  309. write up the information you needed.