Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detect if click was inside react component or not in typescript

I have roughly the following:

componentDidMount() {                                                                                                                                                                                           
  document.querySelector('body')!.addEventListener('click', this.click);
} 

click = (e: Event) => {                                                                                                                                                                                         
  if (this.state.toggled) {
    if (!ReactDom.findDOMNode(this.someRef).contains(e.target)) {
      this.setState({ toggled: false });
    }
  }
};

render() {
  return (<CustomElement
    ref={(e) => { this.someRef = e; }}                                                                                                                                                                   
  />)
}

This code correctly detects whether the user clicks inside or outside the CustomElement, so far so good.

However, tsc isn't happy with this at all:

error TS2345: Argument of type 'EventTarget' is not assignable to parameter of type 'Node'.
  Property 'attributes' is missing in type 'EventTarget'.

Looking at node_modules/typescript/lib/lib.d.ts this makes sense, since e.target is an EventTarget which appears to only define the functions for adding and removing event handlers. However MDN says e.target is "A reference to the object that dispatched the event." which sounds closer to what I want.

So how do I retain the currently working functionality, while also making tsc happy (as opposed to just silencing the error)?

like image 550
Letharion Avatar asked May 08 '17 07:05

Letharion


People also ask

How do you detect a click outside the React component?

Detecting an outside click of a functional component Let's build an HTML tooltip by creating a React functional component named InfoBox . The tooltip will appear when the user clicks a button, and it will be closed if the user clicks outside of the tooltip component.

Which component can detect click as event?

jQuery closest() is used to see if the target from a click event has the dom element as one of its parents. If there is a match the click event belongs to one of the children and is thus not considered to be outside of the component. So in my component, I want to attach a click handler to the window .

How do you find a click outside of an element?

To detect click outside element with JavaScript, we can use the element's contains method. const specifiedElement = document. getElementById("a"); document. addEventListener("click", (event) => { const isClickInside = specifiedElement.

How do you check if something is a react component?

We can check if an object is a function component by checking that it's a function and that it contains the 'return React. createElement' code. To check for a class component we can check for type 'function' . And we can check for the isReactComponent property in the component's prototype .


2 Answers

I found a similar discussion in the typescript issue queue which says in this case a type assertion is probably unavoidable:

Basically EventTarget is the most general type, of which Element is a subtype, and HTMLElement is a subtype of that. If you get back a thing from the DOM, we generally have no idea which it is, and you should add a type assertion to "add in" the specific external knowledge that you have about the structure of your particular DOM layout.

It's possible, for example, that the .target of an event is not an Element (you can add event listeners to XMLHttpRequest, for example, and XMLHttpRequest does not have a getBoundingClientRect method).

So because target may or may not be a Node like I want, TS can't infer the truth, and I need to assert.

contains(e.target as Node) appears to be the right solution.

like image 134
Letharion Avatar answered Oct 12 '22 09:10

Letharion


How about defining your click handler like this:

private click = (e: Event) =>
{
    if (e.target instanceof HTMLElement && !ReactDOM.findDOMNode(this.someRef).contains(e.target))
    {
        if (this.state.toggled)
        {
            this.setState({ toggled: false });
        }
    }
}

Sample pen.

like image 14
Amid Avatar answered Oct 12 '22 11:10

Amid