How to continue event propagation after cancelling?

前端 未结 8 687
孤街浪徒
孤街浪徒 2020-12-13 11:41

When a user clicks a certain link I would like to present them with a confirmation dialog. If they click \"Yes\" I would like to continue the original navigation. One catch:

相关标签:
8条回答
  • 2020-12-13 12:23

    We have a similar requirement in our project and this 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 讨论(0)
  • 2020-12-13 12:23

    I edited your code. New features that I added:

    1. Added namespace to event;
    2. After click on element event will be removed by namespace;
    3. Finally, after finish needed actions in "MyApp" section continue propagation by triggering others element "click" events.

    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 讨论(0)
  • 2020-12-13 12:26

    I solved problem by this way on one of my projects. This example works with some basic event handling like clicks etc. Handler for confirmation must be first handler bound.

        // 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 :)
    
    0 讨论(0)
  • 2020-12-13 12:30

    I solved this by:

    1. placing a event listener on a parent element
    2. removing the class from the link ONLY when the user confirms
    3. reclicking the link after I have removed the class.

    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 the event listener to the parent and not the link itself is because the jQuery's on event will bind to the element until told otherwise. So even though the element does not have the class another-page it still has the event listener attached thus you have to take advantage of event delegation to solve this problem.

    GOTCHAS this is very state based. i.e. if you need to ask the user EVERYTIME they click on a link you'll have to add a 2nd listener to readd the another-page class back on to the link. i.e.:

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

    side note you could also remove the event listener on container if the user accepts, if you do this make sure you use namespace events because there might be other listeners on container you might inadvertently remove. see https://api.jquery.com/event.namespace/ for more details.

    0 讨论(0)
  • 2020-12-13 12:32

    This is untested but might 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()
      })
    })
    
    0 讨论(0)
  • 2020-12-13 12:40

    It could be risky but seems to work at the time of writing at least, we're using it in production.

    This is ES6 and React, I have tested and found it working for the below browsers. One bonus is if there is an exception (had a couple during the way making this), it goes to the link like a normal <a> link, but it won't be 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>  
      }
    }
    
    0 讨论(0)
提交回复
热议问题