web-dev-qa-db-fra.com

Comment continuer la propagation d'événement après l'annulation?

Lorsqu'un utilisateur clique sur un certain lien, je souhaite lui présenter une boîte de dialogue de confirmation. S'ils cliquent sur "Oui", j'aimerais continuer la navigation d'origine. Une capture: ma boîte de dialogue de confirmation est implémentée en renvoyant un objet jQuery.Deferred qui est résolu uniquement lorsque/si l'utilisateur clique sur le bouton Oui. La boîte de dialogue de confirmation est donc asynchrone.

Donc, fondamentalement, je veux quelque chose comme ça:

$('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
    })
})

Bien sûr, je pourrais définir un drapeau et re-déclencher le clic, mais c'est vraiment compliqué. Une manière naturelle de faire ça?

52
George Mauer

Vous trouverez ci-dessous les éléments du code qui fonctionnaient dans Chrome 13, à ma grande surprise.

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

Ce n'est pas très multi-navigateur, et sera peut-être corrigé à l'avenir, car cela ressemble à un risque de sécurité, à mon humble avis. 

Et je ne sais pas ce qui se passe si vous annulez la propagation d'événements. 

11
c69

Si je comprends bien le problème, je pense que vous pouvez simplement mettre à jour l'événement pour qu'il s'agisse de l'événement d'origine dans la fermeture que vous avez là. Il suffit donc de définir e = e.originalEvent dans la fonction .done.

https://jsfiddle.net/oyetxu54/

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

voici un violon avec un exemple différent (laissez la console ouverte pour pouvoir voir les messages): cela a fonctionné pour moi en chrome et firefox

3
user2977636

J'ai résolu le problème de cette façon sur l'un de mes projets. Cet exemple fonctionne avec certaines opérations de base telles que les clics, etc. Le gestionnaire de confirmation doit être lié au premier gestionnaire.

    // 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 :)
2
Jan Míšek

Nous avons une exigence similaire dans notre projet et cela fonctionne pour moi. Testé en chrome et 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
Karuna Lakshman

J'ai édité votre code. Nouvelles fonctionnalités que j'ai ajoutées:

  1. Espace de noms ajouté à l'événement;
  2. Après avoir cliqué sur l'élément, l'événement sera supprimé par l'espace de noms.
  3. Enfin, après avoir terminé les actions nécessaires dans la section "MyApp", poursuivez la propagation en déclenchant les événements des autres éléments "click".

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
Юра Косяк

J'ai résolu ceci par:

  1. placer un écouteur d'événement sur un élément parent
  2. supprimer la classe du lien UNIQUEMENT lorsque l'utilisateur confirme
  3. recliquer le lien après avoir supprimé la classe.

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>

La raison pour laquelle j'ai ajouté l'écouteur d'événement au parent et non au lien lui-même, c'est parce que l'événement on de jQuery sera lié à l'élément jusqu'à indication contraire. Ainsi, même si l'élément n'a pas la classe another-page, l'écouteur d'événements est toujours attaché. Vous devez donc tirer parti de event delegation pour résoudre ce problème.

GOTCHAS c'est très basé sur l'état. C'est-à-dire que si vous devez demander à l'utilisateur CHAQUE FOIS, ils cliquent sur un lien, vous devrez ajouter un deuxième écouteur pour relire la classe another-page dans le lien. c'est à dire.:

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

note latérale vous pouvez également supprimer l'écouteur d'événements sur container si l'utilisateur accepte, si vous le faites, assurez-vous d'utiliser les événements namespace car il pourrait y avoir d'autres écouteurs sur le conteneur que vous pourriez supprimer par inadvertance. voir https://api.jquery.com/event.namespace/ pour plus de détails.

0
devkaoru