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.
Related
I think it very stupid question, but after hours of google it - i have no idea or solution.
Point is that i need reload page after handling "click" event on my web-site. In chrome\opera it's works without problems. But in firefox i have some bug. My JS code with comments:
$('#project_create').click(function() {
var projectname = $("#project_name").val();
var projectdescription = $("#project_description").val();
$.post("projects/add_project.php", {
project_name: projectname,
project_description: projectdescription
});
$("#create_project_form").hide("slow");
$("#project_list").show("slow");
//return false; - if it uncomment, all work, but next page reloader doesn't work.
window.location.reload(); //but if it's work, FireFox doesn't send $.post-query
});
I need to work booth methods, because after click - script put in $_SESSION['my_var'] some variable, and it variable is avaliable after reload page only. Maybe there are other ways to do it? As I understand the problem here in features with firefox and preventDefault();
Thanks!
The issue is just you reload the page before performing the ajax request.
Try to reload page in the ajax success callback handler :
$.post("projects/add_project.php", {
project_name: projectname,
project_description: projectdescription
}, function(){
window.location.reload();
});
And remove your old window.location.reload()
When you do a return, code after that line will not be reached anymore and is considered "dead code". One does not simply put code after a return.
Another is that there's and issue when using return false to prevent default default actions. It prevents delegation/bubbling. Event handlers hooked higher up in the DOM tree (especially ones hooked with on()) won't be executed. If delegation matters, don't use it.
If your goal is to prevent the default action of the link and do stuff in JS, use event.preventDefault instead. The event object is passed in as the first argument in the handler.
$('#project_create').click(function(event) {
event.preventDefault();
// rest of the code
});
In addition to what the other answers suggest, you can also execute the location.reload method asynchronously using setTimeout:
setTimeout(function() { location.reload(); }, 1);
return false;
EDIT: The entire idea of running an asynchonous AJAX request and reloading the page immediately afterwards is flawed, of course, since the AJAX request may not have been completed by the time you reload the page.
You should therefore use a callback, as suggested by the accepted answer. Alternatively, you could use a synchronous AJAX request, but that would freeze execution until the request has completed, which is generally not desirable.
I'm trying to execute the following code
window.onunload = function () {
var sTag = document.createElement('script');
sTag.src = 'mysrc';
document.getElementsByTagName('head')[0].appendChild(sTag);
return false; };
}
Itseems to work fine in FF but in chrome I'm getting the download status as cancelled as soon as the unload event is fired. I saw some posts in SO with ajax solutions but I'm executing this script inside a cross domain iframe. Im just trying to log the time for which my api was live in a page per visitor. So, I'm sending some time log information on unload of the page. Is there any work around for the above?
For the purpose of what you described, you can have that script loaded in your page or parent window (you are saying it is an iframe right?) and run a function on window.unload:
window.onunload = function(){
window.top.logtime(); // if it is in the parent, or
window.logtime() //if it is in the same window
};
and don't return false, unload event cannot be cancelled. (in best cases, the user gets an alert dialog that will override the return false statement.)
I think what makes this different is how fast it carries out a new function, before the body gets unloaded. Manipulating the DOM is definitely much slower than making a call.
We're creating a click tracking app, that builds heatmaps. I'm writing a script which users are suppose to insert into their pages for tracking to work.
It works fine on elements, which doesn't require a redirect or form submit. For example, if I click on h1 or p or whatever, it works perfectly correct. But, if I click on a a, request to our server never happens before the normal redirect.
In the last couple of days I tried a lot of ways to do that. First of, I tried a normal AJAX call, since it was a cross-domain request I had to use JSONP, but again, that AJAX call did not have time to execute before the redirect. Adding async: false would have solved the problem, but it doesn't work with JSONP requests. So I decided to add a flag variable which indicates that it is safe to move on with redirect and used an empty while loop to wait until it becomes try in the ajax callback. But the while loop was blocking the execution flow, so callback never got a chance to set that variable to true. Here is some simplified code:
$(document).on('click', function (e) {
//part of the code is omitted
$.ajax({
url: baseUrl,
data: data,
type: "get",
dataType: "jsonp",
crossDomain: true,
complete: function (xhr, status,) {
itsSafeToMoveOn = true;
}
});
while(!itsSafeToMoveOn){}
return true;
});
The next thing I tried is to use unload page event to wait until total ajax calls in progress would become zero (I had a counter implemented) and then to move on with redirect. It worked in Firefox and IE, but in WebKit there was this error:
Error: Too much time spent in unload handler
After that I realized that I don't care about the server response and using img.src for the request would be an ideal fit for this case. So at this point code looks like this:
$(document).click(function (e) {
//part of the code is ommited
(new Image).src = baseUrl + '?' + data;
if (tag === "a" || clickedElement.parents().has("a")) {
sleep(100);
}
return true;
});
That way I increased the overall script performance slightly, but problem with links remains unchanged. The sleep function appears to be also blocking the execution flow and request never happens.
The only idea left is to return false from the event handler and than redirect manually to the clicked element's href or to call submit() on the form, but it will complicate things to much and believe me it's already a huge pain in the ass to debug this script in different browsers.
Does anyone have any other ideas?
var globalStopper = true;
$(document).on('click', function (e) {
if (globalStopper === false)
return true; //proceed with click if stopper is NOT set
else {
globalStopper = false; //release the breaks
$.ajax({
//blahblah
complete: function (xhr, status,) {
$(elem).click(); //when ajax request done - "rerun" the click
}
});
return false; //DO NOT let browser process the click
}
});
Also, instead of adding image, try adding script. And then add the script to the HEAD section. This way the browser will "wait" until it's loaded.
$(document).on('click', function (e) {
var scriptTag = document.createElement("script");
scriptTag.setAttribute("type", "text/javascript");
scriptTag.setAttribute("src", url);
document.getElementsByTagName("head")[0].appendChild(scriptTag);
return true;
}
I would take a look at the navigator sendBeacon API mentioned in this stack overflow answer or directly linked to here.
From the description on the site
navigator.sendBeacon(url, data) - This method addresses the needs of analytics and diagnostics code that typically attempts to send data to a web server prior to the unloading of the document.
You can save information to ajax request in cookies or localStorage and make any worker that will send information. Saving to cookies or localStorage is faster then ajax-request. You can do next:
$(document).click(function (e) {
var queue = localStorage.getItem('requestQueue');
queue.push(data);
localStorage.setItem('requestQueue',queue);
});
$(function(){
setInterval(function(){
var queue = localStorage.getItem('requestQueue');
while (queue.length > 0) {
var data = queue.pop();
$.ajax({
...
success: function(){
localStorage.setItem('requestQueue', queue);
}
});
}
},intervalToSendData);
});
So, when user click on link or send a form, data will be saved to storage and after user go to next page, this worker starts and send data to your server.
The JavaScript is basically executed in single thread. It is not possible to have your callback function executed and at the same time have an infinite loop waiting for a flag variable from it. The infinite loop will occupy the single execution thread and the callback will never be called.
Best approach is to cancel the default handler of your event and bubbling for it (basically return false if you are really building your tracking code with jQuery), and do the necessary actions (redirect page to the necessary address if a link was clicked or trigger other default actions), but this would take a lot of careful work to recreate all the possible combinations of actiona and callbacks.
Another approach is to:
1) Look for something specific to your code in the event data
2) If it is not present - make an AJAX call and in its callback re-trigger the same even on the same element, but this time with your specific bit added to the even data; after the AJAX call return false
3) If your specific bits are present in the data - simply do nothing, allowing the default event processing to take place.
The either approach may bite, however.
So if I understand right, you want your ajax logs completed before the page unloads and follows a link href. This sounds like a perfect case where you could consider using Deferreds in jQuery.
When your user clicks on anything that's supposed to take him away from the page, just check your promise status. If it's not resolved, you could throw a modal window over the page, and ask the user to wait til the progress is complete. Then, add a new pipe to your deferred, telling it to change the location href once everything is complete.
Let me know if this is the scenario. If it is, I'll explain in more detail. No use continuing if I didn't understand your requirement properly
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.
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.