• Stars
    star
    1,818
  • Rank 25,535 (Top 0.6 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created almost 10 years ago
  • Updated 10 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

An onClickOutside wrapper for React components

npm version Build Status npm

⚠️ Open source is free, but developer time isn't ⚠️

This package needs your support to stay maintained. If you work for an organization whose website is better off using react-onclickoutside than rolling its own code solution, please consider talking to your manager to help fund this project. Open Source is free to use, but certainly not free to develop. If you have the means to reward those whose work you rely on, please consider doing so.

An onClickOutside wrapper for React components

This is a React Higher Order Component (HOC) that you can use with your own React components if you want to have them listen for clicks that occur somewhere in the document, outside of the element itself (for instance, if you need to hide a menu when people click anywhere else on your page).

Note that this HOC relies on the .classList property, which is supported by all modern browsers, but not by deprecated and obsolete browsers like IE (noting that Microsoft Edge is not Microsoft Internet Explorer. Edge does not have any problems with the classList property for SVG elements). If your code relies on classList in any way, you want to use a polyfill like dom4.

This HOC supports stateless components as of v5.7.0, and switched to using transpiled es6 classes rather than createClass as of v6.

Sections covered in this README

Installation

Use npm:

$> npm install react-onclickoutside --save

(or --save-dev depending on your needs). You then use it in your components as:

Usage

Functional Component with UseState Hook

This HoC does not support functional components, as it relies on class properties and component instances. However, you almost certainly don't need this HoC in modern (React 16+) functional component code, as a simple function will do the trick just fine. E.g.:

function listenForOutsideClicks(listening, setListening, menuRef, setIsOpen) {
  return () => {
    if (listening) return;
    if (!menuRef.current) return;
    setListening(true);
    [`click`, `touchstart`].forEach((type) => {
      document.addEventListener(`click`, (evt) => {
        if (menuRef.current.contains(evt.target)) return;
        setIsOpen(false);
      });
    });
  }
}

Used in a functional component as:

import React, { useEffect, useState, useRef } from "react";
import listenForOutsideClicks from "./somewhere";

const Menu = () => {
  const menuRef = useRef(null);
  const [listening, setListening] = useState(false);
  const [isOpen, setIsOpen] = useState(false);  
  const toggle = () => setIsOpen(!isOpen);

  useEffect(listenForOutsideClick(
    listening,
    setListening,
    menuRef,
    setIsOpen,
  ));

  return (
    <div ref={menuRef} className={isOpen ? "open" : "hidden"}>
      <h1 onClick={toggle}>...</h1>
      <ul>...</ul>
    </div>
  );
};

export default Menu;

Example: https://codesandbox.io/s/trusting-dubinsky-k3mve

ES6 Class Component

import React, { Component } from "react";
import onClickOutside from "react-onclickoutside";

class MyComponent extends Component {
  handleClickOutside = evt => {
    // ..handling code goes here...
  };
}

export default onClickOutside(MyComponent);

CommonJS Require

// .default is needed because library is bundled as ES6 module
var onClickOutside = require("react-onclickoutside").default;
var createReactClass = require("create-react-class");

// create a new component, wrapped by this onclickoutside HOC:
var MyComponent = onClickOutside(
  createReactClass({
    // ...,
    handleClickOutside: function(evt) {
      // ...handling code goes here...
    }
    // ...
  })
);

Ensuring there is a click handler

Note that if you try to wrap a React component class without a handleClickOutside(evt) handler like this, the HOC will throw an error. In order to use a custom event handler, you can specify the function to be used by the HOC as second parameter (this can be useful in environments like TypeScript, where the fact that the wrapped component does not implement the handler can be flagged at compile-time):

// load the HOC:
import React, { Component } from "react";
import onClickOutside from "react-onclickoutside";

// create a new component, wrapped below by onClickOutside HOC:
class MyComponent extends Component {
  // ...
  myClickOutsideHandler(evt) {
    // ...handling code goes here...
  }
  // ...
}
var clickOutsideConfig = {
  handleClickOutside: function(instance) {
    return instance.myClickOutsideHandler;
  }
};
var EnhancedComponent = onClickOutside(MyComponent, clickOutsideConfig);

Note that if you try to wrap a React component with a custom handler that the component does not implement, the HOC will throw an error at run-time.

Regulate which events to listen for

By default, "outside clicks" are based on both mousedown and touchstart events; if that is what you need, then you do not need to specify anything special. However, if you need different events, you can specify these using the eventTypes property. If you just need one event, you can pass in the event name as plain string:

<MyComponent eventTypes="click" ... />

For multiple events, you can pass in the array of event names you need to listen for:

<MyComponent eventTypes={["click", "touchend"]} ... />

Regulate whether or not to listen for outside clicks

Wrapped components have two functions that can be used to explicitly listen for, or do nothing with, outside clicks

  • enableOnClickOutside() - Enables outside click listening by setting up the event listening bindings.
  • disableOnClickOutside() - Disables outside click listening by explicitly removing the event listening bindings.

In addition, you can create a component that uses this HOC such that it has the code set up and ready to go, but not listening for outside click events until you explicitly issue its enableOnClickOutside(), by passing in a properly called disableOnClickOutside:

import React, { Component } from "react";
import onClickOutside from "react-onclickoutside";

class MyComponent extends Component {
  // ...
  handleClickOutside(evt) {
    // ...
  }
  // ...
}
var EnhancedComponent = onClickOutside(MyComponent);

class Container extends Component {
  render(evt) {
    return <EnhancedComponent disableOnClickOutside={true} />;
  }
}

Using disableOnClickOutside() or enableOnClickOutside() within componentDidMount or componentWillMount is considered an anti-pattern, and does not have consistent behaviour when using the mixin and HOC/ES7 Decorator. Favour setting the disableOnClickOutside property on the component.

Regulate whether or not to listen to scrollbar clicks

By default this HOC will listen for "clicks inside the document", which may include clicks that occur on the scrollbar. Quite often clicking on the scrollbar should close whatever is open but in case your project invalidates that assumption you can use the excludeScrollbar property to explicitly tell the HOC that clicks on the scrollbar should be ignored:

import React, { Component } from "react";
import onClickOutside from "react-onclickoutside";

class MyComponent extends Component {
  // ...
}
var EnhancedComponent = onClickOutside(MyComponent);

class Container extends Component {
  render(evt) {
    return <EnhancedComponent excludeScrollbar={true} />;
  }
}

Alternatively, you can specify this behavior as default for all instances of your component passing a configuration object as second parameter:

import React, { Component } from "react";
import onClickOutside from "react-onclickoutside";

class MyComponent extends Component {
  // ...
}
var clickOutsideConfig = {
  excludeScrollbar: true
};
var EnhancedComponent = onClickOutside(MyComponent, clickOutsideConfig);

Regulating evt.preventDefault() and evt.stopPropagation()

Technically this HOC lets you pass in preventDefault={true/false} and stopPropagation={true/false} to regulate what happens to the event when it hits your handleClickOutside(evt) function, but beware: stopPropagation may not do what you expect it to do.

Each component adds new event listeners to the document, which may or may not cause as many event triggers as there are event listening bindings. In the test file found in ./test/browser/index.html, the coded uses stopPropagation={true} but sibling events still make it to "parents".

Marking elements as "skip over this one" during the event loop

If you want the HOC to ignore certain elements, you can tell the HOC which CSS class name it should use for this purposes. If you want explicit control over the class name, use outsideClickIgnoreClass={some string} as component property, or if you don't, the default string used is ignore-react-onclickoutside.

Older React code: "What happened to the Mixin??"

Due to ES2015/ES6 class syntax making mixins essentially impossible, and the fact that HOC wrapping works perfectly fine in ES5 and older versions of React, as of this package's version 5.0.0 no Mixin is offered anymore.

If you absolutely need a mixin... you really don't.

But how can I access my component? It has an API that I rely on!

No, I get that. I constantly have that problem myself, so while there is no universal agreement on how to do that, this HOC offers a getInstance() function that you can call for a reference to the component you wrapped, so that you can call its API without headaches:

import React, { Component } from 'react'
import onClickOutside from 'react-onclickoutside'

class MyComponent extends Component {
  // ...
  handleClickOutside(evt) {
    // ...
  }
  ...
}
var EnhancedComponent = onClickOutside(MyComponent);

class Container extends Component {
  constructor(props) {
    super(props);
    this.getMyComponentRef = this.getMyComponentRef.bind(this);
  }

  someFunction() {
    var ref = this.myComponentRef;
    // 1) Get the wrapped component instance:
    var superTrueMyComponent = ref.getInstance();
    // and call instance functions defined for it:
    superTrueMyComponent.customFunction();
  }

  getMyComponentRef(ref) {
    this.myComponentRef = ref;
  }

  render(evt) {
    return <EnhancedComponent disableOnClickOutside={true} ref={this.getMyComponentRef}/>
  }
}

Note that there is also a getClass() function, to get the original Class that was passed into the HOC wrapper, but if you find yourself needing this you're probably doing something wrong: you really want to define your classes as real, require'able etc. units, and then write wrapped components separately, so that you can always access the original class's statics etc. properties without needing to extract them out of a HOC.

Which version do I need for which version of React?

If you use React 0.12 or 0.13, version 2.4 and below will work.

If you use React 0.14, use v2.5 through v4.9, as these specifically use react-DOM for the necessary DOM event bindings.

If you use React 15, you can use v4.x, which offers both a mixin and HOC, or use v5.x, which is HOC-only.

If you use React 15.5, you can use v5.11.x, which relies on createClass as supplied by create-react-class rather than React.createClass.

If you use React 16 or 15.5 in preparation of 16, use v6.x, which uses pure class notation.

Support-wise, only the latest version will receive updates and bug fixes.

I do not believe in perpetual support for outdated libraries, so if you find one of the older versions is not playing nice with an even older React: you know what to do, and it's not "keep using that old version of React".

IE does not support classList for SVG elements!

This is true, but also an edge-case problem that only exists for IE11 (as all versions prior to 11 no longer exist), and should be addressed by you, rather than by thousands of individual libraries that assume browsers have proper HTML API implementations (IE Edge has proper classList support even for SVG).

If you need this to work, you can add a shim for classList to your page(s), loaded before you load your React code, and you'll have instantly fixed every library that you might remotely rely on that makes use of the classList property. You can find several shims quite easily, a good one to start with is the dom4 shim, which adds all manner of good DOM4 properties to "not quite at DOM4 yet" browser implementations.

Eventually this problem will stop being one, but in the mean time you are responsible for making your site work by shimming everything that needs shimming for IE. As such, if you file a PR to fix classList-and-SVG issues specifically for this library, your PR will be closed and I will politely point you to this README.md section. I will not accept PRs to fix this issue. You already have the power to fix it, and I expect you to take responsibility as a fellow developer to shim what you need instead of getting obsolete quirks supported by libraries whose job isn't to support obsolete quirks.

To work around the issue you can use this simple shim:

if (!("classList" in SVGElement.prototype)) {
  Object.defineProperty(SVGElement.prototype, "classList", {
    get() {
      return {
        contains: className => {
          return this.className.baseVal.split(" ").indexOf(className) !== -1;
        }
      };
    }
  });
}

I can't find what I need in the README

If you've read the whole thing and you still can't find what you were looking for, then the README is missing important information that should be added in. Please file an issue with a request for additional documentation, describing what you were hoping to find in enough detail that it can be used to write up the information you needed.

More Repositories

1

BezierInfo-2

The development repo for the Primer on Bézier curves, https://pomax.github.io/bezierinfo
HTML
2,328
star
2

bezierjs

A nodejs and client-side library for (cubic) Bezier curve work
JavaScript
1,650
star
3

bezierinfo

A Primer on Bezier Curves
HTML
733
star
4

lib-font

This library adds a new Font() object to the JavaScript toolbox, similar to new Image() for images
JavaScript
725
star
5

nrGrammar

The Nihongo Resources grammar book: "An Introduction to Japanese; Syntax, Grammar & Language"
TeX
327
star
6

node-flickrapi

A node.js (and client-library) implementation of the Flickr API with oauth API key authentication and API method proxying
JavaScript
177
star
7

Pjs-2D-Game-Engine

Processing.js 2D Game Engine
JavaScript
83
star
8

mahjong

Proper mahjong. In Javascript. Because I love this "game".
JavaScript
75
star
9

js-svg-path

A parser that turns SVG path strings into a JS object you can mess with.
JavaScript
62
star
10

music-theory-js

For when you need to do music theory things. In javascript. Don't look at me you know what you need this for.
JavaScript
60
star
11

fontmetrics.js

A JavaScript library that extends the Canvas2D context.measureText(<String>) with additional typographical metrics
JavaScript
56
star
12

svg-path-reverse

This is a JavaScript SVG path to "something" converter, turning a path into hookable graphics instructions for arbitrary conversion
JavaScript
56
star
13

CFF-glyphlet-fonts

Making tiny fonts, because making tiny fonts.
JavaScript
50
star
14

react-component-visibility

For when your components need to know whether users can see them or not
JavaScript
47
star
15

PHP-Font-Parser

This is a PHP code library for parsing TTF/OTF fonts from file.
PHP
45
star
16

A-binary-parser-generator

This project aims to create a tool that can turn a spec file into a parser skeleton for binary data files such as OpenType fonts, PNG images, etc.
JavaScript
38
star
17

nihongoresources.com

A repository of nihongoresources.com related materials.
CSS
37
star
18

Usered

A minimal PHP user authentication system
PHP
36
star
19

midi-with-node

Turn your browser into a MIDI device through the magic of Nodejs, easymidi, and socket.io
JavaScript
31
star
20

photoshop-paths-to-SVG

This is a photoshop "jsx" script that turns all paths in the active document into a single SVG file for use in something else.
JavaScript
30
star
21

node-jp-conjugations

A Japanese verb conjugator and unconjugator
JavaScript
30
star
22

arduino-midi-recorder

Let's build an Arduino-based MIDI recorder!
C++
26
star
23

react-positionable-component

A <Positionable> React component to turn arbitrary content into easy to CSS transform content
JavaScript
22
star
24

socketless

A framework and methodology for writing web socket RPC programs, without writing a single line of web socket or RPC code.
JavaScript
20
star
25

flickrmirror

Mirror your Flickr account to your HD. Then write a new site based on it.
JavaScript
17
star
26

the-cff-table

A markdown conversion of the Adobe Tech notes 5176 and 5177 as a stub for "what the OpenType chapter on the CFF table should look like", for specifically-for-OpenType-only CFF blocks.
17
star
27

RGBAnalyse

An RGB-HC histogram library for same-origin images
JavaScript
16
star
28

EasyPageComments

A simple instant-comments-section for pages based on PHP at the server, and _optionally_ JavaScript at the front end
PHP
16
star
29

LUT-builder

A web based LUT builder for OBS 64x64x64 LUTs
JavaScript
15
star
30

custom-graphics-element

Because what if you could just... write graphics sketches? On the web? Like, directly?
JavaScript
15
star
31

lightroom-catalog-helper

A lightroom catalogue helper utility
JavaScript
15
star
32

node-jp-conversion

Japenese input conversion, turning any string into {kanji, katakana, hiragana, romaji} where possible.
JavaScript
15
star
33

pomax.github.io

My blog, hosted via github
Mathematica
14
star
34

react-formbuilder

A relatively easy to use form builder (single or multipage), with error reporting and lots of room for customization
JavaScript
14
star
35

ucharclasses

A XeLaTeX package that lets you insert arbitrary code between characters from different unicode blocks
TeX
14
star
36

WebGLdraw

LDraw .dat to .obj converter for rendering LEGO using WebGL
JavaScript
13
star
37

Minimal-font-generator

This bit of JavaScript generates fonts that implement a single character, as a zero-width glyph.
JavaScript
13
star
38

msfs-simconnect-api-wrapper

A JavaScripty wrapper around node-simconnect
JavaScript
13
star
39

css-node-graph

A React based, pure CSS graphing thing (note: just graphs. Not charts or plots)
JavaScript
11
star
40

pocketknife

A small JS tookit -- like jQuery, but smaller, not backward compatible, and without the powerhouse functions.
JavaScript
10
star
41

terminal-menu-program

Terminal menu programs, using retro ansi terminal menus for serious 80s technicolor business
JavaScript
10
star
42

filebrowser_s3

an AWS S3 fix for Mezzanine's media manager
Python
9
star
43

tex2utf

A UTF8 massaging of the `tex2mail` utility, found on https://ctan.org/pkg/tex2mail
Perl
9
star
44

CSSmacros

Start writing CSS with macros ("constants" or "set-once variables"), because sometimes classes and/or just elements doesn't cut it
JavaScript
8
star
45

KeyTrap.js

Trap keyboard events in HTML elements to prevent default browser actions (backspace, cursor, etc)
JavaScript
7
star
46

cff-opcode-fonts

OTF fonts for testing CFF opcode support
7
star
47

are-we-flying

Writing auto-pilots in JavaScript for MSFS
JavaScript
7
star
48

webpack-block-loader

A webpack loader for doing generic block-processing. You provide the delimiters and processor, block-loader will find your data and pass it through your processor.
JavaScript
7
star
49

mini-daw

A web based mini-DAW implementation (using the Web Audio and Web MIDI APIs)
JavaScript
7
star
50

Mike-s-Mahjong

A Java implementation of Mahjong (the real game, not the solitaire nonsense)
Java
6
star
51

arctic-redpoll

A JavaScript Tree library
JavaScript
6
star
52

three-point-perspective

Implementing strict three point perspective is ridiculous. You'll never need it. Let's do it anyway.
6
star
53

quick-glyph-maker

A quick glyph maker that I need for writing a blog post on fonts
JavaScript
5
star
54

pagemixer

Like an in-browser dev tools for normal people to play with
JavaScript
5
star
55

FlickrFindr

a javascript flickr search module
JavaScript
5
star
56

Inkcyclopedia

A simple inkcyclopedia based on (at least) 228 ink sample tests
JavaScript
5
star
57

inkdb.org

React overhaul of inkcyclopedia.org
JavaScript
5
star
58

use-models-for-data

Work with your data through well-defined, always-consistent models, instead of rando plain JS objects
JavaScript
4
star
59

processing-bezier-master

Master repo for a generic Processing sketch for Bezier curve work.
Processing
4
star
60

inkdb-data

Open Source Fountain Pen Ink Data
JavaScript
4
star
61

ReactRoutedExample

An example app using React and React Router 2, with webpack bundling, watching, and server-side page generation.
JavaScript
4
star
62

node-type2-charstring

A simple converter for Type2 charstrings in human readable and byte form
JavaScript
4
star
63

console-plot

Add plotting to the browser console, because data viz is life.
JavaScript
3
star
64

mjscoring.js

html5 mahjong scoring app (work in progress)
JavaScript
3
star
65

gh-weblog

A simple weblog that runs entirely off of gh-pages
CSS
3
star
66

simple-cff-builder

A simple CFF builder with global subroutines and human readable charstring input for generating test fonts.
JavaScript
3
star
67

walking-bass

A walking bass generator.
JavaScript
3
star
68

react-circletree

A circle tree component for React applications
JavaScript
3
star
69

reddit-image-catch-up

Catch up on all your favourite image subreddits with one call.
JavaScript
3
star
70

node-jp-giongo

A Japanese onomatopoeia and mimesis module for node.js
JavaScript
3
star
71

node-kanji-relations

A node.js module for getting parent/child grapheme relations for Japanese kanji
JavaScript
3
star
72

mathparser

A math parser for Processing - give it a string, it gives back an evaluator
JavaScript
2
star
73

processing-stubs

Code that I constantly end up writing when using Processing
Processing
2
star
74

socketless-demo

Demos for the socketless library, https://github.com/Pomax/socketless
JavaScript
2
star
75

nrAPI

nihongoresources REST API
JavaScript
2
star
76

canonical-cubic-curve

Canonical cubic curve analysis based on the 1989 paper "A Geometric Characterization of Parametric Cubic Curves"
Processing
2
star
77

php-MJ-Scoring

MJ scoring JS/PHP script/page thingy
PHP
1
star
78

mox-server

A tiny express server that will host mox (emulated S3) data as text/html content
JavaScript
1
star
79

JSON-object-traverse

JS "mix in" to add string based route traversal to a JS object
JavaScript
1
star
80

node-rtltr-for-less

An automatic rtl/ltr extension for less-middleware
JavaScript
1
star
81

Cursive-swash-typeface

I'm desiging a cursive, swashed typeface, and will try to implement it as OTF. We'll see how that goes
1
star
82

Bookstyle

Making text files look pretty and paginated for your reading pleasure. In HTML
1
star
83

NRAPI-React

A React UI implementation for api.nihongoresources.com
JavaScript
1
star
84

Sheet-slicer

A sheet slicer using react and tiny REST API server
JavaScript
1
star
85

Twitter-Personal-Feed-Curator

For bulk deleting about a thousand tweets at a time.
JavaScript
1
star
86

noodle-doodle

What I'd like to see in a piano roll/sequencer when it comes to keyboard usability
JavaScript
1
star
87

Online-stack-validator

This project offers an online stack validator for programming code, analysing matched pairs
JavaScript
1
star