Javascript 取消后如何继续事件传播?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7811959/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to continue event propagation after cancelling?
提问by George Mauer
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: my confirmation dialog is implemented by returning a jQuery.Deferred object which is resolved only when/if the user clicks the Yes button. So basically the confirmation dialog is asynchronous.
当用户单击某个链接时,我想向他们显示一个确认对话框。如果他们点击“是”,我想继续原来的导航。一个问题:我的确认对话框是通过返回一个 jQuery.Deferred 对象来实现的,该对象仅在/如果用户单击“是”按钮时解决。所以基本上确认对话框是异步的。
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 a flag and re-trigger click but that is messy as heck. Any natural way of doing this?
当然,我可以设置一个标志并重新触发点击,但这太麻烦了。这样做的任何自然方式?
回答by c69
Below are the bits from the code that actually worked in Chrome 13, to my surprise.
令我惊讶的是,以下是在 Chrome 13 中实际运行的代码中的部分内容。
function handler (evt ) {
var t = evt.target;
...
setTimeout( function() {
t.dispatchEvent( evt )
}, 1000);
return false;
}
This is not very cross-browser, and maybe will be fixed in future, because it feels like security risk, imho.
这不是很跨浏览器,也许将来会修复,因为感觉像安全风险,恕我直言。
And i don't know what happens, if you cancel event propagation.
如果取消事件传播,我不知道会发生什么。
回答by Jan Mí?ek
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 :)
回答by user2977636
If I am understanding the problem correctly, I think you can just update the event to be the original event in that closure you have there. So just set e = e.originalEvent in the .done function.
如果我正确理解了这个问题,我认为您可以将事件更新为您所在的那个闭包中的原始事件。所以只需在 .done 函数中设置 e = e.originalEvent 。
https://jsfiddle.net/oyetxu54/
https://jsfiddle.net/oyetxu54/
MyApp.confirm("confirmation?")
.done(function(){ e = e.originalEvent;})
here is a fiddle with a different example (keep the console open so you can see the messages): this worked for me in chrome and firefox
这是一个带有不同示例的小提琴(保持控制台打开,以便您可以看到消息):这在 chrome 和 firefox 中对我有用
回答by OZZIE
I think it could be risky(!) but seems to work at the time of writing at least.
我认为这可能有风险(!),但至少在撰写本文时似乎有效。
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.
这是 ES6 和 React,我已经测试并发现它适用于以下浏览器。一个好处是,如果有一个例外(在制作过程中有几个例外),它会像普通<a>
链接一样进入链接,但它不会是 SPA 然后 ofc。
Desktop:
桌面:
- Chrome v.76.0.3809.132
- Safari v.12.1.2
- Firefox Quantum v.69.0.1
- Edge 18
- Edge 17
- IE11
- Chrome v.76.0.3809.132
- Safari v.12.1.2
- 火狐量子 v.69.0.1
- 边缘 18
- 边缘 17
- IE11
Mobile/Tablet:
手机/平板电脑:
- Android v.8 Samsung Internet
- Android v.8 Chrome
- Android v.9 Chrome
- iOs11.4 Safari
- iOs12.1 Safari
- Android v.8 三星互联网
- Android v.8 Chrome
- 安卓 v.9 铬
- 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>
}
}
回答by devkaoru
I solved this by:
我通过以下方式解决了这个问题:
- placing a event listener on a parent element
- removing the class from the link ONLY when the user confirms
- 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.
我将事件侦听器添加到父级而不是链接本身的原因是因为 jQuery 的on
事件将绑定到元素,除非另有说明。因此,即使元素没有类,another-page
它仍然附加了事件侦听器,因此您必须利用event delegation
来解决这个问题。
GOTCHASthis 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.:
GOTCHAS这是非常基于状态的。即,如果您需要在用户每次单击链接时询问他们,您必须添加第二个侦听器以将another-page
类读回链接。IE:
$('body').on('click', function (e) {
$(e.currentTarget).addClass('another-page');
});
side noteyou 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.
旁注,container
如果用户接受,您还可以删除事件侦听器,如果这样做,请确保使用namespace
事件,因为容器上可能还有其他侦听器,您可能会无意中删除。有关更多详细信息,请参阅https://api.jquery.com/event.namespace/。
回答by Karuna Lakshman
We have a similar requirement in our project and this works for me. Tested in chrome and IE11.
我们的项目中有类似的要求,这对我有用。在 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();
})
}
})
回答by Юра Косяк
I edited your code. New features that I added:
我编辑了你的代码。我添加的新功能:
- Added namespace to event;
- After click on element event will be removed by namespace;
- Finally, after finish needed actions in "MyApp" section continue propagation by triggering others element "click" events.
- 为事件添加了命名空间;
- 点击元素事件后会被命名空间移除;
- 最后,在“MyApp”部分完成所需的操作后,通过触发其他元素“点击”事件继续传播。
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");
});
});
回答by irishbuzz
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()
})
})