在 beforeunload 事件处理程序中停止页面卸载
Posted
技术标签:
【中文标题】在 beforeunload 事件处理程序中停止页面卸载【英文标题】:Stop page from unloading within the beforeunload event handler 【发布时间】:2016-06-01 04:26:48 【问题描述】:在用户浏览页面代码之前,检查他是否编辑了某些表单字段。如果他这样做了,我会显示一个带有Yes
和No
按钮的模式窗口。如果他单击“否”,模态应该关闭并且用户留在那个窗口上。如果是 - 保存更改并卸载。
$(window).bind('beforeunload', function()
// check if any field is dirty
if ($('div.form').dirtyForms('isDirty'))
var modalParams =
Content: 'You have some unsaved changes, proceed?'
OnSave: navigateAndSave,
OnCancel: cancelNavigate
ShowModal(modalParams);
)
function navigateAndSave()
// Do stuff
;
function cancelNavigate()
// Stop page from unloading and close the modal
;
那么我可以在cancelNavigate
中做什么来阻止页面卸载?
【问题讨论】:
以后,如果您对jQuery Dirty Forms有任何疑问,请在问题中包含jquery-dirtyforms标签,这样您更有可能得到足够的答案。 【参考方案1】:这是可能的,但前提是用户单击页面内的超链接。如果用户使用浏览器离开页面,他们将获得默认的浏览器对话框,该对话框没有保存选项。
Dirty Forms 自动附加(并删除处理程序)到beforeunload
事件,因此您创建另一个事件处理程序的尝试肯定会失败。使用脏表单时千万不要这样做。
您没有提到您想使用哪个模态对话框框架,所以我将仅展示一个使用 jQuery UI 对话框的示例。集成其他对话框架也是类似的。为此,您可能需要查看existing pre-built dialogs 的源代码。
另外,您的用例有点短。如果用户想要离开并忽略更改怎么办?我添加了一个示例,其中包含一个额外的选项来做到这一点。
<html>
<head>
<link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/jquery.ui/1.11.3/jquery-ui.min.css" />
</head>
<body>
<script type="text/javascript" src="//cdn.jsdelivr.net/g/jquery@1.11.3,jquery.ui@1.11.3,jquery.dirtyforms@2.0.0"></script>
<script type="text/javascript">
$(document).ready(function()
// This is required by jQuery UI dialog
$('body').append('<div id="dirty-dialog" style="display:none;" />');
// This must be called before the first call to .dirtyForms
$(document).bind('bind.dirtyforms', function (ev, events)
var originalOnRefireClick = events.onRefireClick;
events.onRefireClick = function (ev)
if (saveForm)
// TODO: Replace this with your AJAX function
// to save the form.
alert('saving form...');
originalOnRefireClick(ev);
;
);
// Flag indicating whether or not to save the form on close.
var saveForm = false;
$('.mainForm').dirtyForms(
dialog:
// Custom properties to allow overriding later using
// the syntax $.DirtyForms.dialog.title = 'custom title';
title: 'Are you sure you want to do that?',
proceedAndSaveButtonText: 'Save Changes & Continue',
proceedAndCancelButtonText: 'Cancel Changes & Continue',
stayButtonText: 'Stay Here',
preMessageText: '<span class="ui-icon ui-icon-alert" style="float:left; margin:2px 7px 25px 0;"></span>',
postMessageText: '',
width: 650,
// Dirty Forms Methods
open: function (choice, message)
$('#dirty-dialog').dialog(
open: function ()
// Set the focus on close button. This takes care of the
// default action by the Enter key, ensuring a stay choice
// is made by default.
$(this).parents('.ui-dialog')
.find('.ui-dialog-buttonpane button:eq(2)')
.focus();
,
// Whenever the dialog closes, we commit the choice
close: choice.commit,
title: this.title,
width: this.width,
modal: true,
buttons: [
text: this.proceedAndSaveButtonText,
click: function ()
// Indicate the choice is the proceed action
choice.proceed = true;
// Pass a custom flag to indicate to save the data first
// in the onRefireClick event
saveForm = true;
$(this).dialog('close');
,
text: this.proceedAndCancelButtonText,
click: function ()
// Indicate the choice is the proceed action
choice.proceed = true;
// Pass a custom flag to indicate not to save the data
// in the onRefireClick event
saveForm = false;
$(this).dialog('close');
,
text: this.stayButtonText,
click: function ()
// We don't need to take any action here because
// this will fire the close event handler and
// commit the choice (stay) for us automatically.
$(this).dialog('close');
]
);
// Inject the content of the dialog using jQuery .html() method.
$('#dirty-dialog').html(this.preMessageText + message + this.postMessageText);
,
close: function ()
// This is called by Dirty Forms when the
// Escape key is pressed, so we will close
// the dialog manually. This overrides the default
// Escape key behavior of jQuery UI, which would
// ordinarily not fire the close: event handler
// declared above.
$('#dirty-dialog').dialog('close');
);
);
</script>
Change one of the fields below, then click "Go to Google" to try to navigate away.
<form class="mainForm" action="jqueryui.html">
First name: <input type="text" name="fname"><br>
Last name: <input type="text" name="lname"><br>
<input type="submit" value="Submit">
</form>
<a href="http://www.google.com/">Go to Google</a>
</body>
</html>
该示例使用custom event binding 附加到onRefireClick
事件处理程序,当choice.proceed = true
时会触发该事件处理程序,但在false
时不会触发。
【讨论】:
【参考方案2】:这是不可能的。
您只能显示一条短信以及在页面上继续或离开的选项。
请查看此链接了解更多信息:https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload
window.onbeforeunload = funcRef
funcRef
是对函数或函数表达式的引用。 该函数应为Event
对象的returnValue
属性分配一个字符串值并返回相同的字符串。例子:
window.onbeforeunload = function(e) return 'Dialog text here.'; ;
您可以在用户决定留在页面上后,显示您的模态,但我认为它的提议届时将被否决。
正如其他人所说,您可以通过侦听页面上链接和表单上的单击和键事件来拦截导航,但这将不允许您在用户尝试手动输入 URL 时显示任何对话框,请关闭选项卡、关闭窗口、按下重新加载、后退或前进按钮等。
beforeunload
事件旨在保护用户免受恶意脚本的侵害。
如果允许这种控制,某人可以将您的浏览器锁定在所需页面中,不允许您导航离开或关闭窗口。
编辑:
通过附加到beforeunload
和unload
两个事件,我设法显示了一个确认对话框,该对话框的工作方式几乎与您想要的相同。它适用于 Windows 的 Internet Explorer 11、Firefox 40 和 Safari 5.1(我现在可以确认):
var alreadyTriggered = false;
function onQuit()
if (alreadyTriggered) return true;
alreadyTriggered = true;
if (confirm('Sure?'))
alert('OK');
else
alert('Cancel');
window.onbeforeunload = onQuit;
window.onunload = onQuit;
You can <a href="javascript: location.reload();">reload the page</a> to test it here.
这取决于确认对话框导致的 JS 事件循环中断,并且无法使用自定义对话框进行此操作,因为页面将在用户有机会与之交互之前更改或关闭。
仍然无法避免页面更改或关闭,也无法在 Chrome 中进行此操作。这在 Firefox 中也不起作用,因为事件不是由用户交互触发的。
【讨论】:
是否有可能调用“留在此页面上”并自动取消它? 不幸的是,这也是不可能的。请记住,所有这些限制都是一种内在的安全措施。 否决这一点,因为这对于脏表单是可能的,而对于beforeunload
事件是不可能的。 Dirty Forms 能够在用户尝试通过单击页面内的超链接进行导航的情况下显示 GUI 模式对话框。
仅使用 JS 就可以通过“单击”超链接来触发此操作。为此不需要任何框架或库。可以拦截浏览链接和表单,但问题标题中明确说明的 OP 必须在 beforeunload
事件中。我不会要求您删除反对票,但我会要求您在反对票之前多注意所写的内容。【参考方案3】:
我相信您只需要在事件上调用 preventDefault。
$(window).bind('beforeunload', function(e)
// check if any field is dirty
if ($('div.form').dirtyForms('isDirty'))
var modalParams =
Content: 'You have some unsaved changes, proceed?'
OnSave: navigateAndSave,
OnCancel: e.preventDefault();
ShowModal(modalParams);
)
function navigateAndSave()
// Do stuff
;
【讨论】:
以上是关于在 beforeunload 事件处理程序中停止页面卸载的主要内容,如果未能解决你的问题,请参考以下文章
Cordova/Phonegap - beforeunload 工作或任何替代方案?
如何在 React 中使用窗口的 beforeunload 事件执行 axios 代码