How to continue spreading events after cancellation?

When a user clicks on a specific link, I would like to submit them using the confirmation dialog box. If they click Yes, I would like to continue the original navigation. One catch: my confirmation dialog is implemented by returning a jQuery.Deferred object, which is only allowed when / if the user clicks the "Yes" button. Thus, basically the confirmation dialog is asynchronous.

So basically I want something like this:

$('a.my-link').click(function(e) { e.preventDefault(); e.stopPropogation(); MyApp.confirm("Are you sure you want to navigate away?") .done(function() { //continue propogation of e }) }) 

Of course, I could set the flag and re-run the click, but it's dirty as hell. Any natural way to do this?

+59
javascript dom event-propagation
Oct 18 '11 at 18:29
source share
8 answers

Below are snippets of code that really worked in Chrome 13, to my surprise.

 function handler (evt ) { var t = evt.target; ... setTimeout( function() { t.dispatchEvent( evt ) }, 1000); return false; } 

This is not a very cross browser and may be fixed in the future because it seems like a security risk, imho.

And I do not know what will happen if you cancel the distribution of the event.

+10
Oct 18 2018-11-21T00:
source share

I solved the problem this way in one of my projects. This example works with some basic event handling, such as clicks, etc. The handler for confirmation must be associated with the first handler.

  // This example assumes clickFunction is first event handled. // // you have to preserve called function handler to ignore it // when you continue calling. // // store it in object to preserve function reference var ignoredHandler = { fn: false }; // function which will continues processing var go = function(e, el){ // process href var href = $(el).attr('href'); if (href) { window.location = href; } // process events var events = $(el).data('events'); for (prop in events) { if (events.hasOwnProperty(prop)) { var event = events[prop]; $.each(event, function(idx, handler){ // do not run for clickFunction if (ignoredHandler.fn != handler.handler) { handler.handler.call(el, e); } }); } } } // click handler var clickFunction = function(e){ e.preventDefault(); e.stopImmediatePropagation(); MyApp.confirm("Are you sure you want to navigate away?") .done(go.apply(this, e)); }; // preserve ignored handler ignoredHandler.fn = clickFunction; $('.confirmable').click(clickFunction); // a little bit longer but it works :) 
+3
May 17 '12 at 8:37
source share

If I understand the problem correctly, I think you can simply update the event to be the original event in this closure that you have. So just set e = e.originalEvent in the .done function.

https://jsfiddle.net/oyetxu54/

 MyApp.confirm("confirmation?") .done(function(){ e = e.originalEvent;}) 

here is a fiddle with another example (keep the console open so you can see the messages): this worked for me in chrome and firefox

+3
May 11 '15 at
source share

I think this can be risky (!), But it seems to work at the time of this writing.

These are ES6 and React, I tested and found that it works for the following browsers. One of the bonuses is that if there is an exception (when creating which there was a couple), it goes by the link, like a regular <a> link, but there will be no SPA, then ofc.

Desktop:

  • Chrome v.76.0.3809.132
  • Safari v.12.1.2
  • Firefox Quantum v.69.0.1
  • Edge 18
  • Edge 17
  • IE11

Mobile / Tablet:

  • Android v.8 Samsung Internet
  • Android v.8 Chrome
  • Android v.9 Chrome
  • iOs11.4 Safari
  • iOs12.1 Safari

.

 import 'mdn-polyfills/MouseEvent'; // for IE11 import React, { Component } from 'react'; import { Link } from 'react-router-dom'; class ProductListLink extends Component { constructor(props) { super(props); this.realClick = true; this.onProductClick = this.onProductClick.bind(this); } onProductClick = (e) => { const { target, nativeEvent } = e; const clonedNativeEvent = new MouseEvent('click', nativeEvent); if (!this.realClick) { this.realClick = true; return; } e.preventDefault(); e.stopPropagation(); // @todo what you want before the link is acted on here this.realClick = false; target.dispatchEvent(clonedNativeEvent); }; render() { <Link onClick={(e => this.onProductClick(e))} > Lorem </Link> } } 
+1
Sep 20 '19 at 10:29
source share

I solved this:

  • placing an event listener on the parent element
  • removing a class from a link ONLY when the user confirms
  • link rollback after class removal.

 function async() { var dfd = $.Deferred(); // simulate async setTimeout(function () { if (confirm('Stackoverflow FTW')) { dfd.resolve(); } else { dfd.reject(); } }, 0); return dfd.promise(); }; $('.container').on('click', '.another-page', function (e) { e.stopPropagation(); e.preventDefault(); async().done(function () { $(e.currentTarget).removeClass('another-page').click(); }); }); $('body').on('click', function (e) { alert('navigating somewhere else woot!') }); 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div class="container"> <a href="#" class="another-page">Somewhere else</a> </div> 

The reason I added an event listener to the parent element, and not to the link itself, is because the jQuery on event will bind to the element until it says otherwise. Therefore, even if an element does not have another-page class, it still has an event plug-in, so you should use event delegation to solve this problem.

GOTCHAS , it is very condition based. that is, if you need to ask the user EVERYTIME, they click on the link, you will have to add a second listener to read the another-page class back to the link. i.e:.

 $('body').on('click', function (e) { $(e.currentTarget).addClass('another-page'); }); 

note that you can also remove the event listener on container , if the user accepts, if you do, make sure you use namespace events, because there may be other listeners in the container that you can accidentally delete. See https://api.jquery.com/event.namespace/ for more details.

0
May 20 '15 at
source share

We have a similar requirement in our project, and it works for me. Tested in Chrome and IE11.

 $('a.my-link').click(function(e) { e.preventDefault(); if (do_something === true) { e.stopPropogation(); MyApp.confirm("Are you sure you want to navigate away?") .done(function() { do_something = false; // this allows user to navigate $(e.target).click(); }) } }) 
0
Aug 09 '17 at 10:48 on
source share

I edited your code. New features I added:

  1. Added namespace to the event;
  2. After clicking on an element, the event will be deleted by the namespace;
  3. Finally, after completing the necessary steps in the MyApp section, continue distributing, triggering click events of other elements.

The code:

 $('a.my-link').on("click.myEvent", function(e) { var $that = $(this); $that.off("click.myEvent"); e.preventDefault(); e.stopImmediatePropagation(); MyApp.confirm("Are you sure you want to navigate away?") .done(function() { //continue propogation of e $that.trigger("click"); }); }); 
0
Dec 14 '18 at 6:43
source share

This is untested, but may serve as a workaround for you.

 $('a.my-link').click(function(e) { e.preventDefault(); e.stopPropogation(); MyApp.confirm("Are you sure you want to navigate away?") .done(function() { //continue propogation of e $(this).unbind('click').click() }) }) 
-2
Oct 18 '11 at 18:46
source share



All Articles