Как продолжить распространение событий после отмены?
Когда пользователь нажимает на определенную ссылку, я хотел бы представить их с помощью диалогового окна подтверждения. Если они нажмут "Да", я бы хотел продолжить оригинальную навигацию. Один улов: мой диалог подтверждения реализуется путем возвращения объекта jQuery.Deferred, который разрешен только тогда, когда/если пользователь нажимает кнопку "Да". Таким образом, в основном диалог подтверждения является асинхронным.
Итак, в основном я хочу что-то вроде этого:
$('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
})
})
Конечно, я мог бы установить флаг и повторно запускать клик, но это грязно, как черт. Любой естественный способ сделать это?
Ответы
Ответ 1
Ниже приведены фрагменты кода, который действительно работал в Chrome 13, к моему удивлению.
function handler (evt ) {
var t = evt.target;
...
setTimeout( function() {
t.dispatchEvent( evt )
}, 1000);
return false;
}
Это не очень кросс-браузер и, возможно, будет исправлено в будущем, потому что это похоже на риск безопасности, imho.
И я не знаю, что произойдет, если вы отмените распространение события.
Ответ 2
Я решил проблему таким образом в одном из моих проектов. Этот пример работает с некоторыми базовыми обработками событий, такими как клики и т.д. Обработчик для подтверждения должен быть связан с первым обработчиком.
// 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 :)
Ответ 3
Если я правильно понимаю проблему, я думаю, вы можете просто обновить событие, чтобы быть оригинальным событием в этом закрытии, которое у вас есть. Поэтому просто установите e = e.originalEvent в .done-функцию.
https://jsfiddle.net/oyetxu54/
MyApp.confirm("confirmation?")
.done(function(){ e = e.originalEvent;})
вот скрипка с другим примером (держите консоль открытой, чтобы вы могли видеть сообщения):
это сработало для меня в chrome и firefox
Ответ 4
Я думаю, что это может быть рискованно (!), Но, похоже, работает на момент написания статьи.
Это ES6 и React, я протестировал и обнаружил, что он работает для следующих браузеров. Одним из бонусов является то, что если есть исключение (при создании которого была пара), оно идет по ссылке, как обычная ссылка <a>
, но не будет SPA, тогда ofc.
Desktop:
- Chrome v.76.0.3809.132
- Safari v.12.1.2
- Firefox Quantum v.69.0.1
- Край 18
- Край 17
- IE11
Mobile/Tablet:
- Android v.8 Samsung Интернет
- 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>
}
}
Ответ 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>
Ответ 6
У нас есть аналогичное требование в нашем проекте, и это работает для меня. Протестировано в хроме и 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();
})
}
})
Ответ 7
Я отредактировал твой код. Новые функции, которые я добавил:
- Добавлено пространство имен к событию;
- После нажатия на элемент событие будет удалено пространством имен;
- Наконец, после завершения необходимых действий в разделе "MyApp" продолжайте распространение, вызывая события "щелчка" других элементов.
Код:
$('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");
});
});
Ответ 8
Это непроверено, но может служить обходным путем для вас
$('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()
})
})