LP13
LP13

Reputation: 34149

How to stop propagating from event handler in JavaScript?

I have a button that triggers an event on a click. Then I have a subscriber to that event. Inside the subscriber's event handler if certain condition is true then I want to stop processing everything inside button's click event.

I tried calling e.preventDefault(), e.stopPropagation() and e.stopImmediatePropagation() but nothing works.

$("#btn").click(function() {
  // trigger event
  console.log("triggering event");

  $(document).trigger("response.beforeSave");

  //I want to stop processing after this when subscriber invokes preventDefault() or 
  //stopPropagation()

  console.log("after trigger. This should not get invoked.");
})

$(document).off("response.beforeSave").on("response.beforeSave", function(e) {
  console.log("start subscriber");

  if (true) // if condition is true
  {
    //e.preventDefault();
    //e.stopPropagation();
    e.stopImmediatePropagation();
    return;
  }

  console.log("exit subscriber. This should not get invoked.");
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="btn" type="button">Click Me</button>

Upvotes: 0

Views: 252

Answers (2)

fdomn-m
fdomn-m

Reputation: 28621

You should create your own Event Object and pass that to the .trigger rather than a string with the event name.

This will allow you to check what happened to the event.

An example exists on the jQuery trigger page

var event = jQuery.Event( "submit" );
$( "form" ).first().trigger( event );
if ( event.isDefaultPrevented() ) {
  // Perform an action...
}

Here's your code updated to match:

$("#btn").click(function(e) {
  // trigger event
  console.log("triggering event");

  // create a new event object 
  var beforeSaveEvent = jQuery.Event("response.beforeSave");
  $(document).trigger(beforeSaveEvent);
  
  if (beforeSaveEvent.isImmediatePropagationStopped()) {
    console.log("event stopped");
    
    // can also check beforeSaveEvent.isDefaultPrevented
    // can also check beforeSaveEvent.isPropagationStopped
    
    // e is the click event - function(e) above
    // could also use `event` here
    // "cancel" the click event    
    e.stopPropagation();
    return;
  }

  console.log("after trigger. This should not get invoked.");
})

$(document).off("response.beforeSave").on("response.beforeSave", function(e) {
  console.log("start subscriber");

  if (true) // if condition is true
  {
    // whichever is used, check the equivalent event.isXXX
    //e.preventDefault();
    //e.stopPropagation();
    e.stopImmediatePropagation();
    return;
  }

  console.log("exit subscriber. This should not get invoked.");
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="btn" type="button">Click Me</button>

Upvotes: 1

Roko C. Buljan
Roko C. Buljan

Reputation: 206565

To put it simple, here's a suggestion by passing arbitrary data (a boolean in your case) via the second parameter of .trigger("EventNamespace", [])

$("#btn").on("click", function(evt) { 
  const canSave = document.querySelector("[name=canSave]").checked;
  $(document).trigger("response.beforeSave", [{canSave}]);

  console.log(`Handler before: canSave is ${canSave}`);
  if (!canSave) return;
  console.log(`Handler after`);
});

$(document).on("response.beforeSave", function(evt, data) {
  if (!data.canSave) return;
  console.log(`Subscriber: canSave is ${data.canSave}`);
});
<label><input type="checkbox" name="canSave"> toggle "canSave"</label><br>
<button id="btn" type="button">Click Me</button>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

PS: place $(document).trigger before (like in the example) or after the if(canSave) statement - depending on what you need.

Upvotes: 1

Related Questions