Warn User Before Leaving Web Page with Unsaved Changes

Warn user before leaving web page with unsaved changes

Short, wrong answer:

You can do this by handling the beforeunload event and returning a non-null string:

window.addEventListener("beforeunload", function (e) {
var confirmationMessage = 'It looks like you have been editing something. '
+ 'If you leave before saving, your changes will be lost.';

(e || window.event).returnValue = confirmationMessage; //Gecko + IE
return confirmationMessage; //Gecko + Webkit, Safari, Chrome etc.
});

The problem with this approach is that submitting a form is also firing the unload event. This is fixed easily by adding the a flag that you're submitting a form:

var formSubmitting = false;
var setFormSubmitting = function() { formSubmitting = true; };

window.onload = function() {
window.addEventListener("beforeunload", function (e) {
if (formSubmitting) {
return undefined;
}

var confirmationMessage = 'It looks like you have been editing something. '
+ 'If you leave before saving, your changes will be lost.';

(e || window.event).returnValue = confirmationMessage; //Gecko + IE
return confirmationMessage; //Gecko + Webkit, Safari, Chrome etc.
});
};

Then calling the setter when submitting:

<form method="post" onsubmit="setFormSubmitting()">     
<input type="submit" />
</form>

But read on...

Long, correct answer:

You also don't want to show this message when the user hasn't changed anything on your forms. One solution is to use the beforeunload event in combination with a "dirty" flag, which only triggers the prompt if it's really relevant.

var isDirty = function() { return false; }

window.onload = function() {
window.addEventListener("beforeunload", function (e) {
if (formSubmitting || !isDirty()) {
return undefined;
}

var confirmationMessage = 'It looks like you have been editing something. '
+ 'If you leave before saving, your changes will be lost.';

(e || window.event).returnValue = confirmationMessage; //Gecko + IE
return confirmationMessage; //Gecko + Webkit, Safari, Chrome etc.
});
};

Now to implement the isDirty method, there are various approaches.

You can use jQuery and form serialization, but this approach has some flaws. First you have to alter the code to work on any form ($("form").each() will do), but the greatest problem is that jQuery's serialize() will only work on named, non-disabled elements, so changing any disabled or unnamed element will not trigger the dirty flag. There are workarounds for that, like making controls readonly instead of enabling, serializing and then disabling the controls again.

So events seem the way to go. You can try listening for keypresses. This event has a few issues:

  • Won't trigger on checkboxes, radio buttons, or other elements that are being altered through mouse input.
  • Will trigger for irrelevant keypresses like the Ctrl key.
  • Won't trigger on values set through JavaScript code.
  • Won't trigger on cutting or pasting text through context menus.
  • Won't work for virtual inputs like datepickers or checkbox/radiobutton beautifiers which save their value in a hidden input through JavaScript.

The change event also doesn't trigger on values set from JavaScript code, so also won't work for virtual inputs.

Binding the input event to all inputs (and textareas and selects) on your page won't work on older browsers and, like all event handling solutions mentioned above, doesn't support undo. When a user changes a textbox and then undoes that, or checks and unchecks a checkbox, the form is still considered dirty.

And when you want to implement more behavior, like ignoring certain elements, you'll have even more work to do.

Don't reinvent the wheel:

So before you think about implementing those solutions and all required workarounds, realize you're reinventing the wheel and you're prone to running into problems others have already solved for you.

If your application already uses jQuery, you may as well use tested, maintained code instead of rolling your own, and use a third-party library for all of this.

jquery.dirty (suggested by @troseman in the comments) provides functions for properly detecting whether a form has been changed or not, and preventing the user from leaving the page while displaying a prompt. It also has other useful functions like resetting the form, and setting the current state of the form as the "clean" state. Example usage:

$("#myForm").dirty({preventLeaving: true});

An older, currently abandoned project, is jQuery's Are You Sure? plugin, which also works great; see their demo page. Example usage:

<script src="jquery.are-you-sure.js"></script>

<script>
$(function() {
$('#myForm').areYouSure(
{
message: 'It looks like you have been editing something. '
+ 'If you leave before saving, your changes will be lost.'
}
);
});

</script>

Custom messages not supported everywhere

Do note that since 2011 already, Firefox 4 didn't support custom messages in this dialog. As of april 2016, Chrome 51 is being rolled out in which custom messages are also being removed.

Some alternatives exist elsewhere on this site, but I think a dialog like this is clear enough:

Do you want to leave this site?

Changes you made may not be saved.

Leave Stay

Jquery before leave the page unsaved changes alert

First of all, You are mixing pears and apples, jquery dialog and onbeforeunload are two diferent things. I think you want to close the dialog and not the window (title of question is misleading).

You don't need to track every mousedown event, you need to call function on dialog before close event.

In beforeClose event of dialog check it there is dirty fields, if there is a dirty fields pop up confirmation message, otherwise return true and dialog will call close event.

Something like this (not tested):

$("#dialog").dialog({
beforeClose: function(){
var isDirty = //logic for dirty check of inputs
if(isDirty){
if(confirm("Are you sure you want to close without saving?")){
return true;
} else {
return false;
}
}

return true;
}
});

Of course im using plain browser confirmation so you will need to use your own logic for dialog confirmation. Hope it helps!

Warning user before closing or leaving page with unsaved changes' doesn't work

I have implemented that way, and it worked:

var somethingChanged=false;
$('#managerForm input').change(function() {
somethingChanged = true;
});
$(window).bind('beforeunload', function(e){
if(somethingChanged)
return "You made some changes and it's not saved?";
else
e=null; // i.e; if form state change show warning box, else don't show it.
});
});

The thing is to binding changed event of my form field with form-id.

How to warn user before leaving page but not on redirect

Because you may want the event bound in some circumstances but not others within the same window, you'll have to not only add the event handler to the window, but you'll have to remove it as well (under the right circumstance) because even though you are changing the URL of the document loaded in the window, you are not changing the window itself:

function handleBeforeUnload (e) {
e.preventDefault();
e.returnValue = '';
}

//Only warn if user is on a New or Edit page
if(location.href.indexOf("/new") !== -1 || location.href.indexOf("/edit") !== -1 {
window.addEventListener('beforeunload', handleBeforeUnload);
} else {
// Remove the previously registered event handler (if any)
window.removeEventListener('beforeunload', handleBeforeUnload);
}

Blazor WebAssembly App - Warn user on navigation - warn user before leaving web page with unsaved changes

Found a similar question that had tried window.onbeforeunload:

Blazor Navigationmanager cancel navigation on locationchanged

There does not seem to be a fix for this at the moment but a location changing event for NavigationManger is committed for .NET 6.0. I hope it will be possible to catch this event on a global level in the application.

https://github.com/dotnet/aspnetcore/issues/14962

Warn user before leaving web page if changes haven't been saved

Turns out I was doing a couple things wrong. My guess is adding the document.getElementById("submit").onclick under "use strict" either caused an error (https://www.w3schools.com/js/js_strict.asp) or caused a problem with the detection since simply adding the function even without the && !btn_click caused it to not work. I also had to change if (modified_inputs.size && !btn_click) { to if (modified_inputs.size >> 0 && !btn_click) {.

In the end, the solution that ended up working for me is as follows:

<script>
var btn_click = false;

function save() {
btn_click = true;
}

"use strict";
(() => {
const modified_inputs = new Set;
const defaultValue = "defaultValue";
// store default values
addEventListener("beforeinput", (evt) => {
const target = evt.target;
if (!(defaultValue in target || defaultValue in target.dataset)) {
target.dataset[defaultValue] = ("" + (target.value || target.textContent)).trim();
}
});
// detect input modifications
addEventListener("input", (evt) => {
const target = evt.target;
let original;
if (defaultValue in target) {
original = target[defaultValue];
} else {
original = target.dataset[defaultValue];
}
if (original !== ("" + (target.value || target.textContent)).trim()) {
if (!modified_inputs.has(target)) {
modified_inputs.add(target);
}
} else if (modified_inputs.has(target)) {
modified_inputs.delete(target);
}
});
addEventListener("beforeunload", (evt) => {
if (modified_inputs.size >> 0 && !btn_click) {
const unsaved_changes_warning = "Changes you made may not be saved.";
evt.returnValue = unsaved_changes_warning;
return unsaved_changes_warning;
}
});
addEventListener("")
})();
</script>

Then add your onclick to the element:

<button type="submit" class="w3-button w3-right w3-theme" id="button" onclick="save()">Save</button>

Warn user of unsaved changes before leaving page

To also cover guards against browser refreshes, closing the window, etc. (see @ChristopheVidal's comment to Günter's answer for details on the issue), I have found it helpful to add the @HostListener decorator to your class's canDeactivate implementation to listen for the beforeunload window event. When configured correctly, this will guard against both in-app and external navigation at the same time.

For example:

Component:

import { ComponentCanDeactivate } from './pending-changes.guard';
import { HostListener } from '@angular/core';
import { Observable } from 'rxjs/Observable';

export class MyComponent implements ComponentCanDeactivate {
// @HostListener allows us to also guard against browser refresh, close, etc.
@HostListener('window:beforeunload')
canDeactivate(): Observable<boolean> | boolean {
// insert logic to check if there are pending changes here;
// returning true will navigate without confirmation
// returning false will show a confirm dialog before navigating away
}
}

Guard:

import { CanDeactivate } from '@angular/router';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';

export interface ComponentCanDeactivate {
canDeactivate: () => boolean | Observable<boolean>;
}

@Injectable()
export class PendingChangesGuard implements CanDeactivate<ComponentCanDeactivate> {
canDeactivate(component: ComponentCanDeactivate): boolean | Observable<boolean> {
// if there are no pending changes, just allow deactivation; else confirm first
return component.canDeactivate() ?
true :
// NOTE: this warning message will only be shown when navigating elsewhere within your angular app;
// when navigating away from your angular app, the browser will show a generic warning message
// see http://stackoverflow.com/a/42207299/7307355
confirm('WARNING: You have unsaved changes. Press Cancel to go back and save these changes, or OK to lose these changes.');
}
}

Routes:

import { PendingChangesGuard } from './pending-changes.guard';
import { MyComponent } from './my.component';
import { Routes } from '@angular/router';

export const MY_ROUTES: Routes = [
{ path: '', component: MyComponent, canDeactivate: [PendingChangesGuard] },
];

Module:

import { PendingChangesGuard } from './pending-changes.guard';
import { NgModule } from '@angular/core';

@NgModule({
// ...
providers: [PendingChangesGuard],
// ...
})
export class AppModule {}

NOTE: As @JasperRisseeuw pointed out, IE and Edge handle the beforeunload event differently from other browsers and will include the word false in the confirm dialog when the beforeunload event activates (e.g., browser refreshes, closing the window, etc.). Navigating away within the Angular app is unaffected and will properly show your designated confirmation warning message. Those who need to support IE/Edge and don't want false to show/want a more detailed message in the confirm dialog when the beforeunload event activates may also want to see @JasperRisseeuw's answer for a workaround.



Related Topics



Leave a reply



Submit