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

How to continue event propagation after cancelling?

本文关键字:事件 继续 传播 何继续 取消      更新时间:2023-09-26

当用户单击某个链接时,我想向他们展示一个确认对话框。如果他们点击"是",我想继续原来的导航。注意:我的确认对话框是通过返回一个jQuery来实现的。延迟对象,只有当/当用户单击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
    })
})

当然,我可以设置一个标志和重新触发点击,但这是混乱的见鬼。有什么自然的方法吗?

下面是实际在Chrome 13中工作的代码,让我感到惊讶。

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

这不是很跨浏览器,也许会在未来修复,因为它感觉像安全风险,恕我直言。

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

这可能有风险,但至少在编写时似乎可行,我们正在生产中使用它。

这是ES6和React,我已经测试并发现它适用于以下浏览器。一个额外的好处是,如果有一个异常(在此过程中有一对),它会像正常的<a>链接一样进入链接,但它不会是SPA,然后是ofc。

桌面:

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

手机/平板电脑:

    Android v.8
  • 三星网络
  • 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>  
  }
}

在我的一个项目中,我用这种方法解决了一个问题。这个例子与一些基本的事件处理,如点击等。用于确认的处理程序必须是第一个绑定的处理程序。

    // 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 :)

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

https://jsfiddle.net/oyetxu54/

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

这里是一个不同的例子(保持控制台打开,这样你就可以看到消息):

我解决了这个问题:

  1. 在父元素上放置事件监听器
  2. 仅当用户确认
  3. 时才从链接中删除类
  4. 删除类后重新点击链接。

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/

在我们的项目中有类似的要求,这对我来说是有效的。在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();
    })
  }
})

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

  1. 点击后元素事件将被命名空间移除;
  2. 最后,在"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");
    });
});

这是未经测试的,但可以作为您的解决方案

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