Web sites such as Facebook implements a confirmation to leave the current page when there is unsaved data. This is not at all hard to do, but it did take me some experimentations to find out what works. I’m discussing my findings in this post.
I’m using the
Jinja templating engine
to generate HTML pages. For client-side, I’m using
JQuery.
I’m mentioning the use of the
Jinja templating engine
is for the shake of completeness, whatever methods employed to generate dynamic HTML
are irrelevant, since what we’re discussing is purely client-side.
To implement this leave page confirmation, we need to handle the
Window: beforeunload event.
I’d like to do it for all pages
across the site, so I implement it in the base template:
File base_template.html
<!doctype html>
<html lang="en" class="h-100">
<head>
...
<script src="http://localhost/work/jquery/js/jquery-3.6.0.min.js"></script>
...
<script>
// Individual pages must set and reset this during their operations.
let dataChanged = false;
const setDataChanged = () => dataChanged = true;
const resetDataChanged = () => dataChanged = false;
$( document ).ready( function(event) {
$( window ).on( 'beforeunload', function( event ) {
if ( dataChanged )
// return confirm( 'There are unsaved data. Please confirm.' );
// return false;
// return true;
return null;
});
});
</script>
</head>
<body class="d-flex flex-column h-100">
{% block content %}
<!-- Content of specific individual pages go here. -->
{% endblock %}
</body>
</html>
The only thing
“Jinja” about this page is the following block:
{% block content %}
<!-- Content of specific individual pages go here. -->
{% endblock %}
Basically, at runtime the processing engine replaces this block
with a requested page to make a complete and valid HTML page
to return to the requesting client.
The codes for the
beforeunload event is simple: if variable
dataChanged is
true, then do a
return null, otherwise nothing.
I have three ( 3 )
return statements commented out: they have the same effect as
return null.
It’s the responsibility of the pages to set and reset
dataChanged by calling
setDataChanged() and
resetDataChanged() respectively.
Please note that when the page is first loaded,
dataChanged is initialised to
false.
The general structure
of individual pages that go into
base_template.html‘s block content:
File a_specific_page.html
{% extends "page_template.html" %}
{% block content %}
<script>
function bindDataChange() {
$( '.selector-input, input[type="checkbox"]' ).on( 'change', function(event) {
...
setDataChanged();
event.preventDefault();
});
};
function bindSave() {
function savedSuccessful( data ) {
resetDataChanged();
...
};
$( '#saveBtn' ).on( 'click', function( even ) {
if ( !$('#somethingFrm').parsley().validate() ) return false;
// Send data to server using AJAX. Response is JSON status.
saveData( '/save-something/', '{{ csrf_token() }}',
$( '#somethingFrm' ).serialize(), savedSuccessful );
event.preventDefault();
});
};
$( document ).ready( function() {
...
bindDataChange();
bindSave();
...
});
</script>
<div class="d-flex row justify-content-center h-100">
...
</div>
{% endblock %}
Note where
setDataChanged() and
resetDataChanged() get called.
Please also recall that when the page is first loaded,
dataChanged is initialised to
false.
This
a_specific_page.html saves data via
AJAX call, the response is a
JSON status, which indicates the outcome of the request: the
page does not get reloaded during this operation.
In short, whenever users do anything on the screen which requires
data to be persisted,
setDataChanged() must be called. And after data has been
successfully saved,
resetDataChanged() must get called.
What about form submission?
Form submission means leaving the current page,
the
beforeunload event could potentially be triggered.
The page segment below shows how I solve this issue:
I must submit forms manually, in this case via
function bindUpdateBtn(). This gives me a chance
to call
resetDataChanged() — even though the data has not been saved —
before unloading the page so the event will not be triggered.
If you have noticed, each page has two ( 2 )
$(document).ready() declarations, browsers seem to allow this,
so far I don’t have any complaint. But I am not sure if this is a
good implementation?
I have tested this approach with the following browsers:
FireFox — Version: 106.0.3 (64-bit)
Opera — Version: 92.0.4561.21, System: Windows 10 64-bit
Chrome — Version 106.0.5249.119 (Official Build) (64-bit)
Edge — Version 106.0.1370.52 (Official build) (64-bit)
I’ve also tested it with browsers’ tab closing, and it also works.
I’m not at all sure if this is the best implementation or not, but
it seems to work for me. If problems arise later on, I’ll do an update on it.
Thank you for reading. I do hope you find this post useful. Stay safe as
always.