[reactjs] React prevent event bubbling in nested components on click

Here's a basic component. Both the <ul> and <li> have onClick functions. I want only the onClick on the <li> to fire, not the <ul>. How can I achieve this?

I've played around with e.preventDefault(), e.stopPropagation(), to no avail.

class List extends React.Component {
  constructor(props) {
    super(props);
  }

  handleClick() {
    // do something
  }

  render() {

    return (
      <ul 
        onClick={(e) => {
          console.log('parent');
          this.handleClick();
        }}
      >
        <li 
          onClick={(e) => {
            console.log('child');
            // prevent default? prevent propagation?
            this.handleClick();
          }}
        >
        </li>       
      </ul>
    )
  }
}

// => parent
// => child

This question is related to reactjs onclick event-propagation

The answer is


This is an easy way to prevent the click event from moving forward to the next component and then call your yourFunction.

<Button onClick={(e)=> {e.stopPropagation(); yourFunction(someParam)}}>Delete</Button>

This is not 100% ideal, but if it is either too much of a pain to pass down props in children -> children fashion or create a Context.Provider/Context.Consumer just for this purpose), and you are dealing with another library which has it's own handler it runs before yours, you can also try:

   function myHandler(e) { 
        e.persist(); 
        e.nativeEvent.stopImmediatePropagation();
        e.stopPropagation(); 
   }

From what I understand, the event.persist method prevents an object from immediately being thrown back into React's SyntheticEvent pool. So because of that, the event passed in React actually doesn't exist by the time you reach for it! This happens in grandchildren because of the way React handle's things internally by first checking parent on down for SyntheticEvent handlers (especially if the parent had a callback).

As long as you are not calling persist on something which would create significant memory to keep creating events such as onMouseMove (and you are not creating some kind of Cookie Clicker game like Grandma's Cookies), it should be perfectly fine!

Also note: from reading around their GitHub occasionally, we should keep our eyes out for future versions of React as they may eventually resolve some of the pain with this as they seem to be going towards making folding React code in a compiler/transpiler.


The new way to do this is a lot more simple and will save you some time! Just pass the event into the original click handler and call preventDefault();.

clickHandler(e){
    e.preventDefault();
    //Your functionality here
}

You can avoid event bubbling by checking target of event.
For example if you have input nested to the div element where you have handler for click event, and you don't want to handle it, when input is clicked, you can just pass event.target into your handler and check is handler should be executed based on properties of target.
For example you can check if (target.localName === "input") { return}.
So, it's a way to "avoid" handler execution


React uses event delegation with a single event listener on document for events that bubble, like 'click' in this example, which means stopping propagation is not possible; the real event has already propagated by the time you interact with it in React. stopPropagation on React's synthetic event is possible because React handles propagation of synthetic events internally.

stopPropagation: function(e){
    e.stopPropagation();
    e.nativeEvent.stopImmediatePropagation();
}

I had issues getting event.stopPropagation() working. If you do too, try moving it to the top of your click handler function, that was what I needed to do to stop the event from bubbling. Example function:

  toggleFilter(e) {
    e.stopPropagation(); // If moved to the end of the function, will not work
    let target = e.target;
    let i = 10; // Sanity breaker

    while(true) {
      if (--i === 0) { return; }
      if (target.classList.contains("filter")) {
        target.classList.toggle("active");
        break;
      }
      target = target.parentNode;
    }
  }

On the order of DOM events: CAPTURING vs BUBBLING

There are two stages for how events propagate. These are called "capturing" and "bubbling".

               | |                                   / \
---------------| |-----------------   ---------------| |-----------------
| element1     | |                |   | element1     | |                |
|   -----------| |-----------     |   |   -----------| |-----------     |
|   |element2  \ /          |     |   |   |element2  | |          |     |
|   -------------------------     |   |   -------------------------     |
|        Event CAPTURING          |   |        Event BUBBLING           |
-----------------------------------   -----------------------------------

The capturing stage happen first, and are then followed by the bubbling stage. When you register an event using the regular DOM api, the events will be part of the bubbling stage by default, but this can be specified upon event creation

// CAPTURING event
button.addEventListener('click', handleClick, true)

// BUBBLING events
button.addEventListener('click', handleClick, false)
button.addEventListener('click', handleClick)

In React, bubbling events are also what you use by default.

// handleClick is a BUBBLING (synthetic) event
<button onClick={handleClick}></button>

// handleClick is a CAPTURING (synthetic) event
<button onClickCapture={handleClick}></button>

Let's take a look inside our handleClick callback (React):

function handleClick(e) {
  // This will prevent any synthetic events from firing after this one
  e.stopPropagation()
}
function handleClick(e) {
  // This will set e.defaultPrevented to true
  // (for all synthetic events firing after this one)
  e.preventDefault()  
}

An alternative that I haven't seen mentioned here

If you call e.preventDefault() in all of your events, you can check if an event has already been handled, and prevent it from being handled again:

handleEvent(e) {
  if (e.defaultPrevented) return  // Exits here if event has been handled
  e.preventDefault()

  // Perform whatever you need to here.
}

For the difference between synthetic events and native events, see the React documentation: https://reactjs.org/docs/events.html


Examples related to reactjs

Error: Node Sass version 5.0.0 is incompatible with ^4.0.0 TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received type undefined raised when starting react app Template not provided using create-react-app How to resolve the error on 'react-native start' Element implicitly has an 'any' type because expression of type 'string' can't be used to index Invalid hook call. Hooks can only be called inside of the body of a function component How to style components using makeStyles and still have lifecycle methods in Material UI? React Hook "useState" is called in function "app" which is neither a React function component or a custom React Hook function How to fix missing dependency warning when using useEffect React Hook? Unable to load script.Make sure you are either running a Metro server or that your bundle 'index.android.bundle' is packaged correctly for release

Examples related to onclick

How to use onClick with divs in React.js React prevent event bubbling in nested components on click React onClick function fires on render Run php function on button click Passing string parameter in JavaScript function Simple if else onclick then do? How to call a php script/function on a html button click Change Button color onClick RecyclerView onClick javascript get x and y coordinates on mouse click

Examples related to event-propagation

React prevent event bubbling in nested components on click Stop mouse event propagation Prevent scrolling of parent element when inner element scroll position reaches top/bottom? How do I prevent a parent's onclick event from firing when a child anchor is clicked? event.preventDefault() vs. return false