I have a custom web part placed on one of the application page in SharePoint. This page it seems already have a function which gets executed on windows beforeunload Javascript event.
My problem is that I too need to execute some client side code (to prompt user for any unsaved changes in my web part) on windows beforeunload event.
How can I achieve this? I mean let the default event be fired as well as call my function also?
Appreciate any help.
This should be possible by checking for an existing handler assigned to the onbeforeunload event and, if one exists, saving a reference to it before replacing with your own handler.
Your web part might emit the following script output to do this:
<script type="text/javascript">
// Check for existing handler
var fnOldBeforeUnload = null;
if (typeof(window.onbeforeunload) == "function") {
fnOldBeforeUnload = window.onbeforeunload;
}
// Wire up new handler
window.onbeforeunload = myNewBeforeUnload;
// Handler
function myNewBeforeUnload() {
// Perform some test to determine if you need to prompt user
if (unsavedChanges == true) {
if (window.confirm("You have unsaved changes. Click 'OK' to stay on this page.") == true) {
return false;
}
}
// Call the original onbeforeunload handler
if (fnOldBeforeUnload != null) {
fnOldBeforeUnload();
}
}
</script>
This should allow you to inject your own logic into the page and make your own determination as to what code executes when the page unloads.
Related
I need to fire a function when the DOM ready or page view changed on single-page applications, here is my code and it works for the first time the user visits the site (DOM ready), but it didn't work when a user switches SPA pages (view changed).
I need a common method for most websites and SPA projects since this code needs to be executed on each of our client's websites(sort of like Google Analytics tracking code), and this code need executed every time when the end-users load or switch the pages. Is there any pure Javascript that can detect when the SPA changes pages?
function docReady(fn) {
if (document.readyState === "complete" || document.readyState === "interactive") {
setTimeout(fn, 1);
} else {
document.addEventListener("DOMContentLoaded", fn);
}
}
docReady(function() {
_AcodeInit();
});
function _AcodeInit()
{
...
}
Option 1: Listening to Changes in URL
As SPAs use routing, you could ideally listen to url changes.
One crude way of doing this is to keep polling using setInterval and track the changes to the url. If the url has changed you could run your handler. However this is wasteful.
Given the lack of options you can listen to all events which could cause transitions, (e.g. mouse clicks, keyboard Enter/Space key press etc.) and in the handler check if the url has changed. Use requestAnimationFrame so that we don't execute handler prematurely.
let currentUrl = location.href;
const checkPageTransition = () => {
requestAnimationFrame(() => {
if (currentUrl !== location.href) {
console.log("url changed");
}
currentUrl = location.href;
}, true);
};
document.body.addEventListener("click", checkPageTransition);
document.body.addEventListener("keyup", e => {
if (e.code === "Enter" || e.code === "Space") checkPageTransition()
});
Option 2: Listening to popstate event
If you are using modern SPAs, they would most likely be using history.pushState and history.popState to manage routing. We could then listen to the window.popstate event. But there are limitations to this event.
From: https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onpopstate
Note: Calling history.pushState() or history.replaceState() won't trigger a popstate event. The popstate event is only triggered by performing a browser action, such as clicking on the back button (or calling history.back() in JavaScript), when navigating between two history entries for the same document.
However we can monkey patch the history.pushState function so that it always fires the history.onpushstate function.
(function(history) {
var pushState = history.pushState;
history.pushState = function(state) {
if (typeof history.onpushstate == "function")
{
history.onpushstate({
state: state
});
}
return pushState.apply(history, arguments);
}
})(window.history);
You would need to similarly patch the replaceState function as well.
I have an application where a resource is created when the user enters that page. If they do not properly save that resource an API call is called to the backend to delete it. Closing the browser before saving is one scenario.
Here I use WindowEventHandlers.onbeforeunload to listen for a close event and fire a function before the window closes.
$window.onbeforeunload = function (evt) {
if (notSavedFlag == true) {
//call an api function
$http.delete(...)
...
}
}
However, the API call does not fire when closing the browser window (and I put a breakpoint in the backend to check if it fires)
What is wrong with my code?
I have one beforeunload handler, that is called when the user actually navigates away from the page:
$(window).on("beforeunload", function () {
cleanup();
});
Another part of my application might however add another beforeunload handler, that asks the user if he really wants to leave because of an ongoing operation:
$(window).on("beforeunload", function () {
return "Do you really want to leave";
});
How can I assure, that the second handler will always be called first and that the first handler will not be called in case the user decides to stay on the page?
I already tried to use the unload event instead. But this doesn't work since it will not execute my clean-up function and the backend call within that function reliably.
You can use window.confirm in case. As in (pseudo-code)
$(window).on("beforeunload", function () {
var result = window.confirm('Do you really want to quit?');
if (result) {
cleanup();
}
}
This way, if the user wants to quit it will cleanup. Does that work?>
I have set up an unload listener which sets a flag that error handlers from an ajax request check.
jQuery(window).unload(function() {
unloadhappening = true;
});
However, the ajax request can be aborted (when the user navigates to another page) and the error handler for the ajax request invoked before the unload event is fired.
I was wondering could I get an event earlier than unload? Obviously I could put a listener on every link to move from the page but was looking for a neater way if there is one.
Thanks
You could probably use onbeforeunload or $(window).on('beforeunload') but return an empty string from the function to prevent the prompt about navigation.
window.onbeforeunload = function(e) {
unloadhappening = true;
// maybe other logic
return ''; // or maybe return null
}
I haven't tested the solution to avoid the popup box in all browsers, so your milage may vary.
I want to do some stuff when user is leaving a page, I add this code
window.onbeforunload = function (e){
return "You save some unsaved data, Do you want to leave?";
}
This prompt can notify the user and user can stay on the page or leave. But I want more to know whether he leaves or not, and do thing on his decision. I tried this,
window.onbeforunload = function (e){
var event = jQuery.Event(e);
var result = confirm('want to leave?');
if (result == false){
//do sth..
event.preventDefault();
}else{
//do clean up
}
}
But it fails!! It always goes away!
Can any body help me doing this?
The method you use (preventing bubbling of the event) is intentionally not possible, otherwise you could prevent users from leaving your page.
You can achieve something similar to what you want by doing your cleanup onunload, and do the stuff you always want to do onbeforeunload.
But I want more to know whether he leaves or not, and do thing on his decision
If you wanna do something when he leaves, you can do it in unload event. For example, as #Erik Bakker mentioned you can send async events in unload event.
However if you wanna find out if user "stayed", in other words cancelled the leaving process, there is a way as well. It's kinda hackish, but it works.
const doSomethingWhenUserStays = function doSomethingWhenUserStays() {
alert('user stayed!!!');
}
window.addEventListener('beforeunload', function onBeforeUnload(e) {
setTimeout(doSomethingWhenUserStays, 500);
// Dialog text doesn't really work in Chrome.
const dialogText = 'A dialog text when leaving the page';
e.returnValue = dialogText;
return dialogText;
});
Method doSomethingWhenUserStays will be called every time, but if user leaves the page, he won't see what it performed anyway. It can perform asynchronous stuff, synchronous, it doesn't really matter because it's within setTimeout therefore it's out of the normal flow of onBeforeUnload and won't interfere with it.
If you want to perform it ONLY if user really stays on the page it's slightly harder. You'd have to set a global flag that checks whether user reached unload or not and only then call what's inside doSomethingWhenUserStays. Consider the following example.
let hasUserLeft = false;
const doSomethingWhenUserStays = function doSomethingWhenUserStays() {
// Perform the following only if user hasn't left the page
if (!hasUserLeft) {
alert('user stayed!!!');
}
}
window.addEventListener('beforeunload', function onBeforeUnload(e) {
// It won't perform doSomethingWhenUserStays in 500ms right after this is called,
// but instead, it will perform it in 500ms after you click "Stay" or "Leave".
// Therefore, there should be some time for `unload` handler to fire and
// set `hasUserLeft` flag before `doSomethingWhenUserStays` is called.
setTimeout(doSomethingWhenUserStays, 500);
// Dialog text doesn't really work in Chrome.
const dialogText = 'A dialog text when leaving the page';
e.returnValue = dialogText;
return dialogText;
});
window.addEventListener('unload', function onUnload() {
hasUserLeft = true;
});
As far as I have read about this method in different browser forums like MSDN, MozillaDev, etc, this method does not have any callbacks for OK/Cancel. You have this for the confirm dialog but not for this.
This is a security implementation to allow users to have full right about which website they should see. Also, it averts hackers from locking users to their sites.