事半功倍:为您的 ASP.NET/其他 Web 应用程序轻松实现“导航离开”功能






4.91/5 (32投票s)
一个 jQuery 脚本,当页面中输入字段值发生改变时,它能让您显示浏览器的“导航离开”消息,并且您无需编写任何代码!
引言
在您浏览网页时,您一定在某个地方看到过类似以下的消息
您知道这何时发生。如果您正在当前网页中进行某些更改(更改表单数据),并且由于某种原因即将离开页面而未提交更改,则会出现上述消息。很简单。
但是,作为开发人员,这可能不是一个容易实现的功能。乍一看,您似乎必须跟踪每个小表单元素值的更改,并在其中一个发生更改时显示消息。更糟糕的是,并非每个页面都具有相同的输入元素集。因此,跟踪每个页面的更改可能会变得更加困难,并且每个页面可能都需要实现其自己的更改跟踪逻辑。
坦率地说,当我第一次遇到这样的需求时,我心想:“哦,浏览器中一定有一些内置功能。”浏览器应该能够跟踪当前显示页面输入元素的更改(一切都是 HTML,每个对象都可以在 DOM 中找到,对吧?)。所以我可能只需要启用该功能即可。
遗憾的是,我错了。浏览器没有这样的内置功能(我仍然认为它们应该有),因此,是我们(可怜的开发人员)必须跟踪更改并显示那些可爱的(?)消息。
为了实现此功能,大多数时候,我们可能最终会为每个不同页面编写跟踪更改的代码(因为每个页面可能具有不同的输入元素集),或者,我们可能会以更困难的方式(而不是更智能的方式)实现一些逻辑来跟踪输入元素的更改。
所以,即使我不是 JavaScript 或 jQuery 专家,我也考虑尝试以一种通用方式开发一些东西,使我们能够为 ASP.NET Web 应用程序(以及可能适用于任何 Web 平台,无论是 ASP.NET 还是 PHP)实现“导航离开”功能,而无需编写任何实际代码!本文旨在演示这种通用实现。
(是的,您的 Google 搜索中可能会出现一两个“导航离开”jQuery 插件,但我发现它们存在错误或不符合确切要求。)
要求
- 如果用户在 Web 表单(页面)中的任何输入字段中修改了任何值,并且如果他/她执行任何需要离开页面的操作(点击超链接、关闭页面等),则网页应显示一条消息,说明如果他们导航离开,他/她可能会丢失更改。
- 如果用户修改了输入字段中的值,然后由于某种原因撤销了值的更改,然后如果他/她执行任何需要离开页面的操作,则网页不应显示任何“导航离开”消息(因为实际上没有值发生更改)。
- 如果用户提交表单或执行某些操作(可能因页面而异),即使输入元素值已更改,网页也不应显示任何“导航离开”消息。在每个页面中,应易于配置哪些操作不应导致显示“导航离开”消息。
- 任何页面都应易于不使用“导航离开”功能。
- 页面开发人员不应被要求编写任何代码来实施“导航离开”功能。该实现应以“插件”方式开发。
- 插件实现逻辑应该简单(这不是火箭科学)。
示例实现
我开发了一个 jQuery 脚本来实施“导航离开”功能,并在一个示例 ASP.NET 应用程序中使用了它,您可以从上面下载该应用程序
该示例应用程序是一个 ASP.NET 网站,包含以下内容
- “navigateaway.js”是本文所讨论的 jQuery 脚本,“navigateaway.min.js”是同一文件的最小化版本。
- MasterPage.master 是使用 NavigateAway.js 脚本的地方,如下所示
<script language="javascript" src="js/jquery.js"></script>
<script language="javascript" src="js/navigateaway.min.js"></script>
Default.aspx 使用了“导航离开”功能,为此它不需要做任何事情(因为它包含了 MasterPage.master)。Default2.aspx 也包含了 Master 页面,但它不使用“导航离开”功能。为了不使用该功能,它只需调用以下 JavaScript 方法
<script language="javascript">
DisableNavigateAway();
</script>
要查看示例应用程序,请浏览示例应用程序的 Default.aspx。您将看到以下屏幕输出
该页面包含以下输入表单元素
- 一个文本框
- 一个下拉列表
- 一个复选框组
- 一个单选按钮组
- 一个文件选择
它包含以下操作元素,这些元素可能导致用户离开当前页面
- 一个超链接(
Hyperlink
) - 一个普通按钮(
Button
) - 一个提交按钮(
Submit
)
如果用户更改页面中输入元素的任何值,以下操作应显示“导航离开”消息
- 点击超链接(
Hyperlink
) - 点击普通按钮(
Button
) - 关闭页面
但是,即使表单元素值已更改,以下操作也不会显示任何“导航离开”消息
- 点击提交按钮(
Submit
)
如果用户更改了输入元素值,并将其改回初始值,则不会显示“导航离开”消息。
就是这样!随意玩转页面。祝您“导航离开”愉快。
如何使用 NavigateAway 脚本
要实现“导航离开”功能,您所需要做的就是将“navigateaway.min.js”(jQuery 脚本的最小化版本)的引用添加到 HTML(或 XHTML 标记)中,并且大多数情况下,您可能有机会在一个文件中添加该引用(该文件在应用程序中的大多数或所有文件中都被包含或重用),然后就完成了。例如,如果您正在使用 ASP.NET 应用程序,如果应用程序中的所有其他页面都使用该 MasterPage 文件,那么将脚本包含在 MasterPage 文件中就足够了。
<script language="javascript" src="js/navigateaway.min.js"></script>
如果脚本包含在 MasterPage 或公共可重用文件中,则“导航离开”功能将默认开启,适用于使用或包含该文件的每个页面。现在,有些页面您可能不想使用“导航离开”功能。您应该如何配置呢?
幸运的是,这很简单。您只需在 HTML(或 XHTML)中调用以下函数即可实现
<script language="javascript">
DisableNavigateAway();
</script>
如果某个特定页面启用了“导航离开”功能,则它将为用户导致离开当前页面的每个操作开启。有时这可能不合需要。例如,您肯定不希望在填写完表单并按下“提交”按钮,或者点击提交并将表单数据保存到服务器的“链接”或“图片链接”后看到“导航离开”消息。此外,如果用户点击模式弹出确认框中的按钮/链接,您可能也不想显示“导航离开”消息。
navigateaway.js 脚本使配置变得极其简单。您只需为不希望触发“导航离开”操作的元素指定 nonavigate
类即可。
例如,“Default.aspx”有一个提交按钮(Submit
),即使表单中的输入元素值被修改,也不应显示“导航离开”消息。
要配置“Submit
”按钮不触发“导航离开”操作,必须如下指定 nonavigate
类
<input type="submit" id="submit" class="nonavigate" value="submit" />
而且,如果页面中的 HTML (XHTML) 元素指定了 nonavigate
类,则无论表单中的输入值是否更改,它都不会触发“导航离开”操作。
脚本
以下是 jQuery 代码。它很小,并且基于一个简单的逻辑构建。
//Initial form value
var initialValue = '';
//Form value at page navigate away event
var userValue = '';
//Default navigate away message
var NAVIGATE_AWAY_MESSAGE =
"The changes you made will be lost if you navigate away from this page.";
//Flag to track whether onbeforeunload event
//is fired already. Required for IE only
var onBeforeUnloadFired = false;
//Flag to disable onbeforeunload plugin
var DISABLE_ONBEFOREUNLOAD_PLUGIN = false;
//Flag whether source Element class causing the firing
//of OnBeforeUnload event have "nonavigate" class
var IgnoreNavigateForEventSource = false;
//Read initial form values and attach the onbeforeunload event
$(document).ready(function() {
initialValue = GetFormValues();
window.onbeforeunload = handleOnBeforeUnload;
//Detect whether the event source has
//"nonavigate" class specified
$("a,input,img").click(function() {
IgnoreNavigateForEventSource = $(this).hasClass("nonavigate");
});
});
//Disables navigate away feature
function DisableNavigateAway() {
DISABLE_ONBEFOREUNLOAD_PLUGIN = true;
}
//Sets navigate away message
function SetNavigateAwayMessage(message) {
NAVIGATE_AWAY_MESSAGE = message;
}
//Do not show navigate away message for the specified element Id
function IgnoreNavigateAwayFor(elementId) {
$("#" + elementId).addClass('nonavigate');
}
//Reads control values in the form
function GetFormValues() {
var formValues = '';
$.each($('form').serializeArray(), function(i, field) {
if (field.name != '__EVENTVALIDATION'
&& field.name != '__EVENTTARGET'
&& field.name != '__EVENTARGUMENT'
&& field.name != '__VIEWSTATE'
&& field.name != '__VIEWSTATEENCRYPTED') {
var inputField = $("[name=" + field.name + "]");
var displayProperty = $(inputField).css("display");
//Ignore the form element which have style property display="none"
if (displayProperty != "none") {
formValues = formValues + "-" + field.name + ":" + field.value;
}
}
});
//Read the check box element values as these are not
//returned by the form.serializeArray() Jquery method
$(':checkbox').each(function() {
formValues = formValues + "-" + $(this).attr("checked");
});
//Read the check box element values as these are
//not returned by the form.serializeArray() Jquery method
$(':radio').each(function() {
formValues = formValues + "-" + $(this).attr("checked");
});
//Read the check box element values as these are not
//returned by the form.serializeArray() Jquery method
$(':file').each(function() {
formValues = formValues + "-" + $(this).val();
});
return formValues;
}
//Reset the onbeforeunload flag : Required for IE
//only as IE has a bug of firing the onbeforeunload
//event twice
function ResetOnBeforeUnloadFired() {
onBeforeUnloadFired = false;
}
//OnBeforeUnload event handler
function handleOnBeforeUnload(event) {
//Do not show message if plugin is disabled
if (DISABLE_ONBEFOREUNLOAD_PLUGIN) return;
//Execute function if the onbeforeunload not fired already: Required
//for IE only as IE has a bug of firing the onbeforeunload
//event twice
if (!onBeforeUnloadFired) {
onBeforeUnloadFired = true;
if (IgnoreNavigateForEventSource) {
//Reset the flag
IgnoreNavigateForEventSource = false;
return;
}
//Reset the onBeforeUnloadFired flag after
//a few milliseconds. Meanwhile, display the navigate
//away message if the initial form value
//is not same as current form value
//Resetting the onBeforeUnloadFired flag after
//a few milliseconds ensures that if the same event is fired
//twice (In IE), the flag will be reset by this
//time and hence the same event handling codes will
//not be executed
window.setTimeout("ResetOnBeforeUnloadFired()", 10);
userValue = GetFormValues();
//Display navigate away message if the initial
//form value and current form value is not the same
if (userValue != initialValue) {
if (NAVIGATE_AWAY_MESSAGE == "") {
return "The changes you made will " +
"be lost if you navigate away from this page.";
}
else {
return NAVIGATE_AWAY_MESSAGE;
}
}
}
}
实施逻辑
该功能是使用一个非常简单的逻辑开发的,具体如下
- 在页面加载时读取表单中所有输入元素的值,将这些值附加到 JavaScript 变量中,并存储起来(例如,
InitialValues
)。 - 离开当前页面时,如果源元素(导致离开当前页面的事件源元素)具有“
nonavigate
”类,则不执行任何操作。 - 否则,再次读取表单中所有输入元素的值,将这些值附加到另一个 JavaScript 变量(例如,
UserValues
)中,并比较这两个变量的值(InitialValues == UserValues
),如果它们不相同,则显示“导航离开”消息。
读取表单元素值
GetFormValues()
方法读取表单中的输入值并将其附加到变量中。它基本上使用 jQuery 方法序列化输入表单值,如下所示
$('form').serializeArray()
如果该脚本用于 ASP.NET Web Forms 应用程序,则表单中会有一些隐藏的输入元素,我们希望在尝试读取和附加表单的输入字段值时将其过滤掉。这些过滤方法如下:
if (field.name != '__EVENTVALIDATION'
&& field.name != '__EVENTTARGET'
&& field.name != '__EVENTARGUMENT'
&& field.name != '__VIEWSTATE'
&& field.name != '__VIEWSTATEENCRYPTED')
如果 NavigateAway 脚本用于任何其他平台,并且存在任何此类隐藏表单元素,则需要如上所述进行过滤。
$('form').serializeArray()
jQuery 函数的一个限制是,它不返回表单中的一些特殊元素,例如
- 复选框组
- 单选按钮组
- 文件
这些输入元素值需要使用以下代码读取并附加到变量中
//Read the check box element values as these
//are not returned by the form.serializeArray() Jquery method
$(':checkbox').each(function()
{
formValues = formValues + "-" + $(this).attr("checked");
});
//Read the check box element values as these are
//not returned by the form.serializeArray() Jquery method
$(':radio').each(function()
{
formValues = formValues + "-" + $(this).attr("checked");
});
//Read the check box element values as these are not returned
//by the form.serializeArray() Jquery method
$(':file').each(function()
{
formValues = formValues + "-" + $(this).val();
});
处理 IE 浏览器中“onbeforeunload”的双重调用
使用 jQuery,onbeforeunload
事件会如下附加到页面加载事件中:
//Read initial form values and attach the onbeforeunload event
$(document).ready(function() {
initialValue = GetFormValues();
window.onbeforeunload = handleOnBeforeUnload;
//Detect whether the event source has "nonavigate" class specified
$("a,input,img").click(function() {
IgnoreNavigateForEventSource = $(this).hasClass("nonavigate");
});
});
请注意,一个“Click”事件也会附加到每个 <a/>
、<input/>
和 <img/>
元素,以检测事件源(当前导致“导航离开”操作的事件源)是否指定了“nonavigate
”类。如果是这样,这个值将在以后用于确定是否显示“导航离开”消息,即使表单元素值已被用户更改。
不幸的是,在 IE 中,onbeforeunload
事件会触发两次。为了解决这个问题,使用了以下逻辑
- 使用一个标志来判断
onbeforeunload
事件是否已触发(onBeforeUnloadFired
)。 - 在
handleOnBeforeUnload()
方法中,如果onBeforeUnloadFired == true
,则不执行任何操作;否则,设置onBeforeUnloadFired = true
并继续执行其余逻辑。 - 如果表单输入值发生更改,则显示“导航离开”消息。请注意,由于
onBeforeUnloadFired = true
,即使事件在 IE 中再次触发,“导航离开”逻辑也不会再次执行。 - 在一定时间间隔后(确保此时 IE 中第二次触发的事件已处理)通过使用以下代码将
onBeforeUnloadFired = false
重置
window.setTimeout("ResetOnBeforeUnloadFired()", 10);
ResetOnBeforeUnloadFired()
方法如下
function ResetOnBeforeUnloadFired()
{
onBeforeUnloadFired = false;
}
控制“导航离开”功能
为特定页面禁用“导航离开”
包含 navigateaway.js 脚本将默认启用“导航离开”功能。因此,如果 navigateaway.js 脚本包含在公共文件(例如,在 MasterPage 中)中,则所有使用该公共文件的页面都将启用该功能。因此,如果需要,必须有一种方法来为特定页面禁用该功能。
幸运的是,这非常简单。只需在页面标记中调用以下方法即可
<script language="javascript">
DisableNavigateAway();
</script>
动态忽略元素的“导航离开”功能
为元素设置“nonavigate
”类可确保该元素不会触发“导航离开”消息。您可以手动将 nonavigate
类设置到页面中的 HTML/XHTML 标记,或者可以调用以下函数来实现此目的
<script language="javascript">
IgnoreNavigateAwayFor('<%= btnSubmit.ClientID %>');
</script>
IgnoreNavigateAwayFor()
方法只是动态地将 nonavigate
类添加到元素中。该方法定义如下
//Do not show navigate away message for the specified element Id
function IgnoreNavigateAwayFor(elementId) {
$("#" + elementId).addClass('nonavigate');
}
设置自定义“导航离开”消息
默认情况下,“导航离开”消息中会显示一个通用的消息。如果需要,可以按如下方式自定义此消息
<script language="javascript">
SetNavigateAwayMessage("Custom Navigate Away message");
</script>
请尝试一下,并告诉我任何反馈或改进建议。我很乐意听取您的意见!