3

I understand how bubbling and event.stopPropagation() works, but I'm looking for a way to avoid click handlers on all children of a DOM node.

<div onclick="console.log('LOG ME')">
  <div onclick="console.log('DO NOT LOG ME')">Click me</div>
</div>

I want 'LOG ME' to be logged, but 'DO NOT LOG ME' not to be logged.

I found a CSS way using pointer-events: none, works well, however, all my :hover, :focus CSS styles are gone on the child div.

Any ideas?

Edit: Some context. I'm using React. The outer div is a HOC, the inner one is a visible component. I want the HOC to catch all clicks of the child component, and stop click propagation into the child. In React would do:

const HOC = ChildComponent => class extends Component {
  handleClick = event => { /* Do what with event? */ }

  render() {
    return <div onClick={this.handleClick}>
      <ChildComponent />
    </div>;
  }
}

Where ChildComponent is a clickable component

jeanpaul62
  • 9,451
  • 13
  • 54
  • 94

3 Answers3

4

Update (for updated question)

If you are using react then use the Capture at the end of the event so the handler is triggered on the capture phase. (See https://reactjs.org/docs/events.html#supported-events)

So something like

const HOC = ChildComponent => class extends Component {
  handleClick = event => { 
     /* Do what with event? */
     event.stopPropagation();
     /*the rest of the code you want for handling the click*/
  }

  render() {
    return <div onClickCapture={this.handleClick}>
      <ChildComponent />
    </div>;
  }
}

You need to stop the propagation on the event, and you need to use the capture phase of the event, not the bubble.

So instead of using the onclick use the proper way to attach event handlers and for the outer one which you want to be the container use true as the 3rd parameter (the useCapture parameter)

document.querySelector('.outer').addEventListener('click', function(e){
  e.stopPropagation();
  console.log('LOG ME')
}, true);

document.querySelector('.inner').addEventListener('click', function(e){
  console.log('DO NOT LOG ME')
}, false);
<div class="outer">
  <div class="inner">Click me</div>
</div>
Gabriele Petrioli
  • 191,379
  • 34
  • 261
  • 317
0

You could use the capture phase of the event to hook into the click at the parent and stop the propagation to the children.

You should also follow modern standards for event registration and not use inline HTML event attributes.

// Normally, the following would set up event handler that bubbles up through all div elements
// but, you will see that it only fires once, for the parent element...
document.querySelector("div").addEventListener("click", function(evt){
  
    console.log(this);
    evt.stopPropagation();  // Don't allow the event
    
  }, true); // Adding the 3rd argument of true causes the event handler to be registered during the capture phase
<div class="parent">
  <div class="child">Click me</div>
</div>
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
0

This may or may not be applicable to your situation but given the example of nested inline onclick you can remove the nested onclick attribute and event handler won't get called

[].slice.call(document.querySelectorAll('[onclick] [onclick]')).forEach(function(el) {
    el.removeAttribute('onclick')
})
<div onclick="console.log('LOG ME')">
  <div onclick="console.log('DO NOT LOG ME')">Click me</div>
</div>
charlietfl
  • 170,828
  • 13
  • 121
  • 150