Kayote
Kayote

Reputation: 15627

React - Prevent Event Trigger on Parent From Child

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:

  1. 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.

  2. 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?

Upvotes: 134

Views: 121880

Answers (10)

Evgeny Deriglazov
Evgeny Deriglazov

Reputation: 11

I came across similar problem today with my backdrop element and its child. When child element is clicked it's event bubbles up to backdrop and make it close.

I've just wrapped up children element with div element: <div onClick={(e) => e.stopPropagation()}> In this case when click event from child tries to bubble up to parent element it is stopped by the div wrapper onclick handler event.stopPropagation(). Click event from child does not trigger onClick={handleClose} of its parent.

import { ReactNode } from 'react';

interface IProps {
  children?: ReactNode;
  open: boolean;
  handleClose: () => void;
}

function Backdrop({ children, open, handleClose }: IProps) {
  
  return (
    <div
      className={open ? "backdrop visible" : "backdrop"}
      onClick={handleClose}
    >
      <div onClick={(e) => e.stopPropagation()}>
        {children ? children : 'Backdrop'}
      </div>
    </div>
  );
}

export default Backdrop;

Upvotes: 1

Ahmad Raza
Ahmad Raza

Reputation: 1

I had a div on which I set an onClick event. Inside that, there was a child component with a like input of checkbox-type, using e.stopPropagation() for the onChange event. This did not work, so keep in mind that this would work on onClick events only.

Upvotes: -1

user3810914
user3810914

Reputation: 425

In my case in the JSX file: just add onClick={(e) => e.stopPropagation() into a tag. It just affects in child, not the parent.

<td>
  {!item.user_info_user ? null : (
    <a
      href={`${ENVIRONMENT.LINK_TO_PROFILE}${item.user_info_user?.domain_url}`}
      target="_blank"         
      rel="noreferrer"
      onClick={(e) => e.stopPropagation()}
    >
      {item.user_info_user?.domain_url}
    </a>
  )}
</td>

Upvotes: 0

vornies
vornies

Reputation: 195

I just stopped the event on the 'child' div like so this then passed down through children. You can place this 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>


Upvotes: 2

Saif Ehsan
Saif Ehsan

Reputation: 31

I find this solution the cleanest. Thank you JohnsonFashanu!

onClick={e => e.currentTarget === e.target && doSomething(e)}

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.

Upvotes: 3

Johnson Fashanu
Johnson Fashanu

Reputation: 1130

I had the same issue in React and solved it using this solution:

if (e.currentTarget != e.target) return;
...

Upvotes: 32

Gifford N.
Gifford N.

Reputation: 429

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}
/>

Upvotes: 0

Daniel
Daniel

Reputation: 1601

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.

Upvotes: 8

Mayur Nandane
Mayur Nandane

Reputation: 317

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">&times;</span>
          </button>
          <span className="label-text fs-12">
            { displayLabel }
          </span>
      </span>
    );
  }
}

export default LabelCancelable;

Upvotes: 3

Oleksandr T.
Oleksandr T.

Reputation: 77482

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>

Upvotes: 253

Related Questions