5

I'm trying to understand the behavior when an element inside of a <a> have a event.stopPropagation() or event.preventDefault().

In the first case, clicking in the <div> also fires the <a> event, moving it to another page. event.stopPropagation() has no effect.

If I change event.stopPropagation() to event.preventDefault(), when clicking in the div, the console logs "div", the <a> does not fires any event BUT the checkbox stops working. So event.preventDefault() affect a child element.

What I'm missing here?

.html

<a href="#" target="_blank">
  <div>
    <input type="checkbox"/>
  </div>
</a>

.js

var div = document.querySelector('div');

div.addEventListener('click', function(event) {
  event.stopPropagation();
  // event.preventDefault(); # second case
  console.log('div');
});

.css (just to make it easier to see on screen)

a {
  display: block;
  background: black;
  width: 300px;
  height: 100px;
}

div {
  background: red;
  width: 50px;
  height: 50px;
}
rougeth
  • 78
  • 1
  • 5
  • probably, try to use `event.stopImmediatePropagation()` instead, so it will stop propagation immediately. check this - [stopImmediatePropagation](https://developer.mozilla.org/en-US/docs/Web/API/Event/stopImmediatePropagation) – Telman Feb 21 '17 at 22:31
  • @TelmanAgababov it didn't work out, but thanks anyway. – rougeth Feb 24 '17 at 16:36

1 Answers1

2

event.preventDefault() will prevent both the href and checkbox from firing their default events because it is a bubbling event.

"When an event is dispatched to an object that participates in a tree (e.g. an element), it can reach event listeners on that object’s ancestors too. First all object’s ancestor event listeners whose capture variable is set to true are invoked, in tree order. Second, object’s own event listeners are invoked. And finally, and only if event’s bubbles attribute value is true, object’s ancestor event listeners are invoked again, but now in reverse tree order." - https://dom.spec.whatwg.org/#dom-event-preventdefault

When you use event.stopPropagation() you are preventing any parent (or child if bubbling = true) from being notified of it's event. This will not prevent it's default event from being fired.

To check if an event is bubbling you can use var x = event.bubbles; console.log(x);

edit: here's an example of how to use event.preventDefault() and event.stopPropagation() in conjunction -

var div = document.querySelector('div');
var a = document.querySelector('a');
a.addEventListener('click', function(event){
    event.preventDefault();
});
div.addEventListener('click', function(event) {
  event.stopPropagation();
  console.log('div');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#" target="_blank">
  <div>
    <input type="checkbox"/>
  </div>
</a>

This will prevent the click event attached to <a> from reaching anything beyond <div>

<a><div><input></div></a> is ONE element.

<div><input></div> is also ONE element. This means when stopPropagation() is used on <input> the click handler for <input> will be the only one which continues to work.

stopPropagation(); acts upon click handler events, not default browser events. So when you stop propagation on the <input> element, <a> still redirects the user, because that is it's default browser event.

However, console.log('a fired.'); and console.log('div fired'); will not trigger as they are click handlers, not default browser events.

preventDefault(); prevents any default browser actions from being executed, not click handlers.

Since <a><div><input /></div></a> is ONE element, when you use preventDefault(); on <a> it will prevent any default browser actions attached to <a> <div> or <input /> from firing.

so when you add preventDefault() to <input /> and try to click on it, it's state won't be changed, but the <a> link will still function as intended when <a> is clicked.

var input = document.querySelector('div');
var a = document.querySelector('a');
var div = document.querySelector('a');

a.addEventListener('click', function(event){
    //event.preventDefault();
    //event.stopPropagation();
    console.log('a fired.');
});

div.addEventListener('click', function(event) {
  //event.preventDefault();
  //event.stopPropagation(); 
  console.log('div fired.');
});

input.addEventListener('click', function(event) {
  event.preventDefault();
  //event.stopPropagation(); 
  console.log('input fired');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!-- <a><div><input></div></a> is ONE element. -->
<a href="#" target="_blank" style="border:solid 1px #ccc;background:green;">this is a link
    <!-- <div><input></div> is also ONE element. This means when stopPropagation() is used on this element, all click handler events within this element will not trigger, but <a></a> will -->
  <div style="border: solid 1px #2779aa;">
    <input type="checkbox"/>
  </div>
</a>
Inkdot
  • 266
  • 1
  • 6