取消后如何继续事件传播?

时间:2011-10-18 18:29:44

标签: javascript dom event-propagation

当用户点击某个链接时,我想通过确认对话框显示它们。如果他们点击“是”,我想继续原始导航。一个问题:我的确认对话框是通过返回一个jQuery.Deferred对象实现的,该对象仅在用户单击Yes按钮时解析。所以基本上确认对话框是异步的。

基本上我想要这样的东西:

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

当然,我可以设置一个标志并重新触发点击,但这很麻烦。这样做的任何自然方式?

8 个答案:

答案 0 :(得分:11)

以下是Chrome 13中实际工作的代码中的一些内容,令我惊讶。

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

这不是非常跨浏览器,也许将来会修复,因为它感觉像安全风险,imho。

如果您取消事件传播,我不知道会发生什么。

答案 1 :(得分:3)

我通过这种方式在我的一个项目上解决了问题。此示例适用于某些基本事件处理(如点击等)。用于确认的处理程序必须是第一个处理程序绑定。

    // 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 :(得分:3)

如果我正确理解了问题,我认为您可以将事件更新为您在那里的闭包中的原始事件。所以只需在.done函数中设置e = e.originalEvent。

https://jsfiddle.net/oyetxu54/

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

这里有一个不同的例子(保持控制台打开,以便您可以看到消息): 这在chrome和firefox中对我有用

答案 3 :(得分:2)

我认为这可能有风险(!),但至少在撰写本文时似乎可行。

这是ES6和React,我已经测试并发现它适用于以下浏览器。一个好处是,如果有异常(在进行此操作的过程中有几个异常),它会像普通的<a>链接一样进入该链接,但是从那时起它就不会是SPA。

台式机:

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

手机/平板电脑:

  • 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>  
  }
}

答案 4 :(得分:0)

我通过以下方式解决了这个问题:

  1. 在父元素上放置事件侦听器
  2. 仅在用户确认
  3. 时才从链接中删除该类
  4. 在我删除课程后重新点击链接。
  5. 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>

    我将事件监听器添加到父级而不是链接本身的原因是因为jQuery的on事件将绑定到该元素,除非另有说明。因此,即使元素没有类another-page,它仍然附加了事件监听器,因此您必须利用event delegation来解决此问题。

    GOTCHAS 这是非常基于状态的。即如果您需要询问用户他们每次点击链接,您必须添加第二个听众,以便将another-page课程重新读回链接。即:

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

    旁注如果用户接受,您还可以删除container上的事件侦听器,如果这样做,请确保使用namespace事件,因为可能还有其他侦听器在容器上,你可能会无意中删除。有关详细信息,请参阅https://api.jquery.com/event.namespace/

答案 5 :(得分:0)

我们的项目有类似的要求,这对我有用。在chrome和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();
    })
  }

})

答案 6 :(得分:0)

我编辑了您的代码。我添加的新功能:

  1. 为事件添加了名称空间;
  2. 点击元素事件后,名称空间将被删除;
  3. 最后,完成“ MyApp”部分中所需的操作后,通过触发其他元素“ click”事件来继续传播。

代码:

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

答案 7 :(得分:-2)

这是未经测试的,但可能会为您提供解决方法

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