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)?
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.
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 .
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.
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 .
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With