React - Prevent Event Trigger on Parent From Child - javascript

I have this scenario, where when parent element is clicked, it flips to show a child element with different colours. Unfortunately, when the user clicks on one of the colours, the 'click' event on parent is also triggered.
How can I stop the event trigger on parent when the child is clicked?
Possible solutions I am wondering:
CSS?
Append pointer-events : none class to the parent when the child is clicked. However, this would mean that the parent will need to be cleansed of the pointer-events class later.
Using Ref?
Record the ref of the parent React element & upon click on the child, compare the event.target against the ref? I don't like this because I don't like the global ref.
Thoughts and the better solution would be much appreciated. The question is:
How can I stop the event trigger on parent when the child is clicked?

You can use stopPropagation
stopPropagation - Prevents further propagation of the current event in
the bubbling phase
var App = React.createClass({
handleParentClick: function (e) {
console.log('parent');
},
handleChildClick: function (e) {
e.stopPropagation();
console.log('child');
},
render: function() {
return <div>
<p onClick={this.handleParentClick}>
<span onClick={this.handleChildClick}>Click</span>
</p>
</div>;
}
});
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

I had the same issue in React and solved it using the solution bellow:
if(e.currentTarget != e.target ) return;
.......

Another solution is to attach to the event callback on the parent the following:
if(event.target == event.currentTarget){
event.stopPropagation()
....
}
This way you can intercept events, that originated in the attached DOM node and unrelated events are relayed to the next node.

I wanted to invoke function on props but at the same time wanted to stop event propagation from child to parent, here is how its handled
class LabelCancelable extends Component {
handleChildClick(e) {
e.stopPropagation()
}
closeClicked(e, props) {
e.stopPropagation();
props.onCloseClicked()
}
render() {
const {displayLabel} = this.props;
return (
<span className={ "label-wrapper d-inline-block pr-2 pl-2 mr-2 mb-2" } onClick={ this.handleChildClick }>
<button type="button" className="close cursor-pointer ml-2 float-right" aria-label="Close"
onClick={(e) => this.closeClicked(e, this.props) }>
<span aria-hidden="true">×</span>
</button>
<span className="label-text fs-12">
{ displayLabel }
</span>
</span>
);
}
}
export default LabelCancelable;

I find this solution the cleanest. Thank you #JohnFash!
onClick={(event) => event.currentTarget == event.target && doSomething(event)}
Here is an attempt at explaining this in more details:
when your mouse enters the parent element, the currentTarget is set (event), then when it enters the child element, the target changes. If you don't do the check, the parent's onClick triggers because the mouseleave event hasn't triggered.

Old question but had trouble then solved it myself I just stopped the event on the 'child' div like so this then passed down through children, so you can place where you want this to stop a click event;
<div onClick={doThisClickEvent}>
<div onClick={(e) => e.stopPropagation()}>
<div>
<p>This now wont trigger click event</p>
</div>
</div>
</div>

I had this problem with Material-UI DataGrid and solved it using this:
event.defaultMuiPrevented = true;
e.g:
<DataGrid
onCellDoubleClick={(params, event) => {
if (!event.ctrlKey) {
event.defaultMuiPrevented = true;
}
}}
{...data}
/>

Related

How can I trigger a function only when only child divs are clicked using event bubble?

const handleMenu = e => {
console.log(e.target);
e.stopPropagation();
}
I want to console the event when a child div is clicked. for example:
<div onclick={handleMenu}>
<button>My orders</button>
<button>Payment</button>
</div>
So, I want to trigger the handleMenu function only when those buttons are clicked and not to trigger when the area of parent div is getting clicked except those button areas. How can I do it?
and yes I am using reactjs. If any alternative way to do that with reactjs, it will be more helpful.
You can do it in following way. Attach to the event callback on the child like following ..
if(event.currentTarget != event.target ) return;
....
target event = element that triggered event.
currentTarget event = element that has the event listener.
In React,
Add a ref to div element, check if the target is equal to ref to see if it's not self event
const myRef = React.useRef(null);
const handleMenu = (e) => {
myRef.current!==e.target
console.log(myRef.current);
e.stopPropagation();
};
return (
<div ref={myRef} onClick={handleMenu}>
<button>My orders</button>
<button>Payment</button>
</div>
);

How can I get the role of an element through the evt object? Or any other distinguishable properties really

Have a simple onClick event which I would like to see the role of the clicked element. The whole reason for this, is that I have an onClick event on my div that is interfearing with one of its children buttons. I need separate behaviors if the click is on the button within the div or elsewhere within the div.
Here is a sample of what I'm trying to go for:
const App = () => {
const onClickDiv = (evt) => {
if (evt.target.role === 'dropdown') {
console.warn('clicked on dropdown button. Do something different.')
} else {
console.warn('clicked on div, but not on dropdown button')
}
}
const onClickDropdownButton = () => {
console.log('do stuff with dropdown button')
}
return (
<div style={{ border: 'red solid 1px' }} onClick={onClickDiv}>
My clickable div
<div>
<button onClick={onClickDropdownButton} role="dropdown">Dropdown Button</button>
</div>
</div>
)
}
ReactDOM.render(
<App />,
document.getElementById('app')
);
div {
padding: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="app"></div>
Take note that any other check works too. I just need figure out a way to know that the user clicked on the Dropdown button within the div as opposed to elsewhere in that same div.
The tag works, I believe other props also work
const onClickDiv = (evt) => {
if (evt.target.tagName === "BUTTON") {
console.warn("clicked on dropdown button. Do something different.");
} else {
console.warn("clicked on div, but not on dropdown button");
}
};
Name:
if (evt.target.name === "dropBtn") {
console.warn("clicked on dropdown button. Do something different.");
} else {
console.warn("clicked on div, but not on dropdown button");
}
};
But that requires it to be added on the html
<div
name="dropDiv"
style={{ border: "red solid 1px" }}
onClick={onClickDiv}
>
My clickable div
<div>
<button name="dropBtn" onClick={onClickDropdownButton} role="dropdown">
Dropdown Button
</button>
</div>
</div>
I believe all you need to do is stop propagation of the event when you click on the button, therefore you can handle the button click in one event handler and a div click (but not on the button) in the other.
Theres no need to distinguish using a role or anything.
const App = () => {
const onClickDiv = (evt) => {
if (evt.target.role === 'dropdown') {
console.warn('clicked on dropdown button. Do something different.')
} else {
console.warn('clicked on div, but not on dropdown button')
}
}
const onClickDropdownButton = (evt) => {
console.log('do stuff with dropdown button')
evt.stopPropagation();
}
return (
<div style={{ border: 'red solid 1px' }} onClick={onClickDiv}>
My clickable div
<div>
<button onClick={onClickDropdownButton} role="dropdown">Dropdown Button</button>
</div>
</div>
)
}
ReactDOM.render(
<App />,
document.getElementById('app')
);
div {
padding: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="app"></div>
As there are 2 event handlers. One is on div which implies on the div itself and the child components of that div. So when you click on div,the button [ as its the child component of div ] also triggers its event handler. The simplest way is to add the
event.stopPropagation()
on the child component[ button ] not on the parent [ div ]. Actually it stops the triggering of the parent component if both child and parent are having some event handlers. Here's how your event handler should look like :
const onClickDropdownButton = (event) => {
event.stopPropagation();
console.log('do stuff with dropdown button')
}
Remember that event.stopPropagation always stops the event propagation of parent component, which means you cannot apply stopPropagation on parent to stop the child component from triggering its event handler.

Action on blur except when specific element clicked with react OR how to get clicked element onBlur

I want to prevent onBlur event when I click on a specific element:
<input type="text" onBlur={<call when I click outside of element except when I click on a specific element>} />
What you could do is give the element that would stop the call an ID and then check for that ID via event.relatedTarget.id
const doSomething = (event) => {
if(event.relatedTarget.id === "badButton"){
return
}
//Do things
}
return(
<div className="DashboardHeader" onBlur={(e) => doSomething(e)}>
<button id="badButton">newbutton</button>
</div>
)

how to not set off onclick in some parts of div?

I want to set off an onclick when you click the outside of a div but not the div. how can I make it that the onclick function is not set off when clicked in the specific div?
import React from "react"
import { Container, Backdrop, Card } from "./styles"
export default function FocusSneakerCard(props){
function click(){
props.setModal(false)
props.setData({})
}
return(
<Container>
<Backdrop onClick={() => click()}>
<Card>
</Card>
</Backdrop>
</Container>
)
}
PS I'm using styled-components.
I found something called event.stopPropagation() which "prevents further propagation of the current event in the capturing and bubbling phases". You can include event.stopPropagation in an onClick event of the child element like so:
const parentClick = () => {
alert("clicked");
};
const childClick = (e) => {
e.stopPropagation();
};
return (
<div className="outer" onClick={parentClick}>
<div className="inner" onClick={childClick}>
Cant Click
</div>
</div>
);
}
https://codesandbox.io/s/laughing-dubinsky-zw013?file=/src/App.js:101-382
Note: "It does not, however, prevent any default behaviors from occurring; for instance, clicks on links are still processed. If you want to stop those behaviors, see the preventDefault() method." from MDN

How do I get the id of a button that was clicked? ReactJS

I was wondering how I would go about getting the id (becuase it is unknown) of a button that is clicked. So when the button is clicked, I know what the id of that specific button is. There are lots of buttons on the page and I would like to know which button is pressed (they all have unique id's). Currently the buttons look like this:
<button key={uuidv4()} id={this.props.keyword} value={this.props.keyword} onClick={this.props.onClick} className="removeButton">Remove</button>
Well if the elements are nested event.target won't always work since it refers to the target that triggers the event in the first place. See this link for the usage of event.currentTarget, which always refer to the element that the handler is bound to.
Another way to grab hold of the element and its attributes is to use React refs. This is more general when trying to get the DOM element in React.
You can use event.target.id to get the ID of button clicked
class App extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(event) {
const id = event.target.id;
console.log(id);
}
render() {
return (
<button id="unique-id" onClick={this.handleClick}>Button</button>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>
I know this has already been answered but i was working with react and faced similar issue, lost 2 hours trying to figure out why event.target.id was sometimes null or an empty string
this solved it for me:
class Button extends React.Component {
getButtonId = (e) => {
console.log(e.currentTarget.id);
}
render() {
return (
<button id="yourID" onClick={this.getButtonId}>Button</button>
);
}
}
You can use either event.target.[selector goes here] or event.currentTarget.[selector goes here] to solve your issue. See example code below.
class Button extends React.Component {
handleId = (e) => {
console.log(e.target.id);
console.log(e.currentTarget.id);
}
render() {
return (
<button id="yourID" onClick={this.handleId}>Button</button>
);
}
}
The very convenient way (not only for buttons but for list of elements) to do this is using custom attribute data-someName of DOM element and then reach it via event.currentTarget.dataset.someName
const openCard = (event) => {
console.log('event.currentTarget.dataset.id', event.currentTarget.dataset.id); // >> id
//do more actions with id
};
<Card onClick={openCard} data-id={item.id}>
<CardContent />
</Card>;
`
There is several way to do this
With manipulating onClick function
<button key={uuidv4()} id={this.props.keyword} value={this.props.keyword} onClick={(e)=>this.props.onClick(e,this.props.keyword)} className="removeButton">Remove</button>
onClick = (e,id) =>{
console.log(id);
}
Without manipulating onClick function
<button key={uuidv4()} id={this.props.keyword} value={this.props.keyword} onClick={this.props.onClick} className="removeButton">Remove</button>
onClick = (e) =>{
const id = e.target.getAttribute("id");
console.log(id);
}
You can do this:
function getId(id) {
console.log(id);
}
<button key={uuidv4()} id={this.props.keyword} value={this.props.keyword} onClick="getId(this.id)" className="removeButton">Remove</button>
if the function to handle the event is in the same component it is best to use the event.target.id, this will have its limitations when you try accessing a DOM element nested as a child component as event.target.id. Use event.currentTarget.id is nested,
This happens because event.currentTarget
can identify the element that caused the event deep down from the child as it burbles out, while event.target.id will set the target as the child component and will not be able to identify the child component that has that element since it target does not change and hence faulty.
You can watch this youtube video to understand better.
also read from their differences
Your onClick handler should receive the click event, which should include the id: event.target.id.
<button
key={uuidv4()}
id={this.props.keyword}
value={this.props.keyword}
onClick={(event) => /* you want to use event.target.id */}
className="removeButton">Remove</button>
This worked for me:
functionName = (event) => {
const id = event.target.id;
const value = event.target.value;
console.log("Value of the button: ", value)
console.log("Id of the button: ", id)}

Categories

Resources