Ajax inside onbeforeunload is not working the first time - javascript

I have been trying to bind beforeunload event by calling the following script so that I can go to the specified URL through AJAX. The problem is that the AJAX is not working the first time as the URL does not get called when the first time I do the page refresh. The second time ajax works. This problem gets fixed when I set async to false but then the alert popup inside success doesn't show up. I need alert box to also show up in success block.
<script type="text/javascript">
$( document ).ready(function() {
// this method will be invoked when user leaves the page, via F5/refresh, Back button, Window close
$(window).bind('beforeunload', function(event){
// invoke the servlet, to logout the user
$.ajax({
cache: false,
type: "GET",
url: "LogoutController" ,
success: function (data) {
alert("You have been logged out");
}
});
});
});
</script>

beforeunload will wait for the event handler to finish its execution before closing the page. Since an ajax call is asynchronous beforeunload is not going to wait for it to finish (your server however should still get the request). This is the expected behaviour and I don't think they is a way around it.
This behaviour can be reproduces using the following code:
window.onbeforeunload = function () {
console.log("bye");
setTimeout(function () {
console.log("bye1");
}, 200);
console.log("bye2")
};
//bye
//bye2
Also, you should note that, according to MDN the specs states that alert() can be ignored:
Since 25 May 2011, the HTML5 specification states that calls to
window.alert(), window.confirm(), and window.prompt() methods may be
ignored during this event.
When this happens on chrome (only browser I checked) you will get the following message in the console:
Blocked alert('test') during beforeunload.

Related

Button click does not fire on second click

I have a lot of buttons with the class search_camera_btn.
When clicking on the button then it submits a form. This step works. On the other side, it should also trigger a button click event.
I wrote the event listener in a coffeescript file which sends Ajax requests, but it only works on the first click.
I put the code in this gist.
The Javascript works when the button is clicked the first time, but fails on subsequent clicks.
Actually I put a alert message in the begin of click event handler,
But it only alerts at the first time.
And there is no error message in my Firbug console. (I thought it is just didn't fire the click event handler.)
$(".search_camera_btn").click ->
alert "test"
There are many buttons,no matter which button I click.
It always works at the first time click.
Here is my more detail source code. download
Any ideas?
I narrow down the buggy code.That is the "Ready to enter start" message only called at the first time.
But there is no error showed on the Firebug Javascript console and Rails console.
Should I enable some settings in the development mode ?
IW2 = get_camera_list: ->
console.log("Start")
ajax_req = $.ajax
url: "update_camera_list/"
type: "GET"
success: (resp) ->
# console.log resp
res = setTimeout (->
ajax_req
), 500
console.log("End")
return
jQuery ->
$(".search_camera_btn").click ->
console.log("Ready to enter start")
IW2.get_camera_list()
Compiled CoffeeScript:
var IW2;
IW2 = {
get_camera_list: function() {
var ajax_req, res;
console.log("Start");
ajax_req = $.ajax({
url: "update_camera_list/",
type: "GET",
success: function(resp) {}
});
res = setTimeout((function() {
return ajax_req;
}), 500);
console.log("End");
}
};
jQuery(function() {
return $(".search_camera_btn").click(function() {
console.log("Ready to enter start");
return IW2.get_camera_list();
});
});
The reason that the handler is only being fired once is because
the ".click" handler only applies to elements that are currently attached
to the DOM. If you replace the HTML after making the AJAX call, the event handlers will be lost.
You should use an event delegate instead. Try this:
jQuery(function() {
return $("body").on("click", ".search_camera_btn", function() {
alert("Ready to enter start");
return IW2.get_camera_list();
});
});
This statement basically says, if there are any elements in the DOM now, or in the future, that have a class of "search_camera_btn", and are contained within the "body" element, then fire the click handler.
I hope this helps!
Change this:
jQuery ->
$(".search_camera_btn").click ->
console.log("Ready to enter start")
IW2.get_camera_list()
For:
jQuery ->
$(".search_camera_btn").click ->
console.log("Ready to enter start")
IW2.get_camera_list()
return
And let me know if it helps ;)
I would make sure nothing else in your application's javascript is failing (like a simple syntax error can cause this sort of thing). Also have you tried this in different browsers? I've had a similar problem where my Ajax would work fine in Chrome but would only post once in Firefox because of some add-ons/extensions (and then I disabled them and they worked).
Also, I'm not sure if I read your gist correctly but it looks like you're specifying .click in both the jQuery of the application.js and in the btn.js.coffee, and I'm pretty sure that section in application.js should just be watching for the element's function/result and not specifying the click again.
If nothing else works, also check out ajax's .done completion call about halfway down the page here: http://api.jquery.com/jQuery.ajax/ . Then show the picture list as a function after .done so you know your ajax post is always completing before moving on to the next thing. (Ajax problems like this often tend to be server side when a call doesn't complete or there's a loop somewhere)

doing an ajax call on window.unload

In my application, there's an object that needs to be ajaxed back to the server before the user switches to another page or closes his browser.
For the moment, I'm using something like this:
$(window).on('unload', function () {
$.ajax(....);
});
Will the ajax call fire in all browsers or are there situations where this will not work and where this situation needs to be handled differently? I don't need to deal with anything in terms of a success function, I'm only concerned about the information making it to the server.
Thanks.
If you're using jQuery, you can set async to false in the ajax call. And it might work, but your results may vary by browser. Here's a jsFiddle example. http://jsfiddle.net/jtaylor/wRkZr/4/
// Note: I came across a couple articles saying we may should to use window.onbeforeunload instead of or in addition to jQuery's unload. Keep an eye on this.
// http://vidasp.net/jQuery-unload.html
// https://stackoverflow.com/questions/1802930/setting-onbeforeunload-on-body-element-in-chrome-and-ie-using-jquery
var doAjaxBeforeUnloadEnabled = true; // We hook into window.onbeforeunload and bind some jQuery events to confirmBeforeUnload. This variable is used to prevent us from showing both messages during a single event.
var doAjaxBeforeUnload = function (evt) {
if (!doAjaxBeforeUnloadEnabled) {
return;
}
doAjaxBeforeUnloadEnabled = false;
jQuery.ajax({
url: "/",
success: function (a) {
console.debug("Ajax call finished");
},
async: false /* Not recommended. This is the dangerous part. Your mileage may vary. */
});
}
$(document).ready(function () {
window.onbeforeunload = doAjaxBeforeUnload;
$(window).unload(doAjaxBeforeUnload);
});
In Google Chrome, the ajax call always completes before I navigate away from the page.
However, I would VERY MUCH NOT RECOMMEND going that route. The "a" in ajax is for "asynchronous", and if you try to force to act like a synchronous call, you're asking for trouble. That trouble usually manifests as freezing the browser -- which might happen if the ajax call took a long time.
If viable, consider prompting the user before navigating away from the page if the page has data that needs to be posted via ajax. For an example, see this question: jquery prompt to save data onbeforeunload
No, unfortunatelly your Ajax call will not get completed as the document will unload during the async call.
You cannot do many things when the user closes the window.
Instead of doing an ajax sync call (deprecated on latest browsers and can get exception), you can open a popup:
$(window).on('unload', function () {
window.open("myscript.php");
});
You can add obviously parameters to the link and you can automatically close the popup if you like.
Popup blocker must be disactivated for your domain in the browser options.
You have to use the onbeforeunload event and make a synchronous AJAX call.
$.ajax({
...
"url": "http://www.example.com",
"async": false,
...
});

Why does `$.post` works only if `alert()` presents after it?

I have the following javascript code:
$.post("<receiver>", postdata);
And gets postdata not always. If I write the following code all works good:
$.post("<receiver>", postdata);
alert('bla-bla-bla, read me for a second');
Why? The page is changing on the save button as the javascript runs. But I need to send post data before redirecting.
You should redirect inside the success callback of your AJAX call:
$.post("<receiver>", postdata, function() {
window.location.href = '...';
});
The reason why your code works if you put an alert immediately after the $.post call is because when this alert pops up, the browser suspends the execution and your AJAX call has enough time to complete.
Don't forget that the first A in AJAX stands for Asynchronous meaning that you could only consume the results returned from the server inside the success callback.
Also if this AJAX call is performed inside some .submit() event handler of a form or inside some .onclick() handler of a submit button or an anchor you should make sure that you have canceled the default action by returning false otherwise your AJAX call will never have the time to execute before the browser redirects away from the page.
Example:
$('#myForm').submit({
$.post("<receiver>", postdata, function() {
...
});
return false; // <!-- That's the important bit
});
Ah, so it seems that the missing portion of your question is you are sending data on click of something yes? Presumably a link? That link causes the browser to follow it immediately, and in your example the alert is delaying the browser enough that your post has enough time to complete.
You need to ensure that the default action of that link is blocked, and do the redirect in the callback of your $.post() instead:
$("a.some_class").click(function(evt)
{
evt.preventDefault(); // makes sure browser doesn't follow the link
// gather your post data here ...
var $this = this;
$.post("<receiver>", postdata, function()
{
window.location.href = $this.attr("href");
});
})
Your alert is causing your script to pause and therefore allowing time for your $.post() to complete.
You should put your redirect script in your $.post() callback.
because it causes a delay. While you press OK the request (which takes at least a few milliseconds) gets finished and the stuff depending on it can follow.
To prevent this, you can pass a callback function that runs after the request got its response.
$.post( url, postdata, function() {
// Success.
} )
The .post is asynchronous.
If you change page during the post process () the POST request will get aborted.
Your alert is preventing this page change
You should replace your .post with a .ajax synchronous request, validating form submission on success ( return true; ) . Or do as suggested by #DarinDimitrov or #Curt

Ajax synchronous with jQuery. Is it possible?

I put a sleep(5) in ajax.php page. I need the code returned to start another function group. It is also using ajax. My first ajax call looks like this:
$.ajax({
url: '/ajax.php',
data: {
id : code
} ,
type: 'POST',
async:false, //<<< here
cache: false,
beforeSend: function(){
$('#loading').dialog();
},
success: function(data){
console.log(data.result);
$('#loading').dialog('close');
initAnotherFunctionGrop(data.result);
},
error: function(){
$('#loading').dialog('close');
}
});
Why I cannot show to the loading message in IE and Chrome? Just Firefox is working with that.
Asynchronous code is best. Synchronous code can hang your browser, which makes it a bad idea in the case of ajax, where the speed of the ajax request depends on factors beyond the users computer and the browser. You don't want the users machine to hang, so avoid it. Instead try something like this.
function a(passedData){
return $.ajax({
url : '/ajax.php',
data : passedData
});
}
function b(passedData){
return $.ajax({
url : '/ajaxB.php',
data : passedData
});
}
$.when(a(data),b(data)).then(function(successDataForA,successDataForB){
//Do code after all asynchronous ajax calls are done.
//As a whole this is still asynchronous so other things can still run
},function(failA,failB){
//This fail callback is not necessary but here it is if needed
});
Use this
$(document).ready(function () {
$('#ajaxloading').hide() // hide it initially
.ajaxStart(function () {
$(this).show();
})
.ajaxStop(function () {
$(this).hide();
});
});
here "ajaxloading" is the Id of the DIV, which you want to display or hide. U can put any content inside this div
If your loading image is gif image, then its hard to show it in IE and chrome, as these browsers stop any changes to DOM component while synchronous call and once the code is executed it shows all the changes.
You can test it by putting an alert box just after you load an image.
$('#loading').dialog();
alert('loading image');
Once alert it popup, you can now see loading image in both IE and chrome as alert stop thread execution until a response is given by user.
Read this link:
[https://stackoverflow.com/questions/11946828/loading-gif-image-is-not-showing-in-ie-and-chrome]
I've had problems in the past getting IE to show a "loading..." message during an Ajax call even with an async call (which is what I'd certainly recommend you use), where the same code did work in FF.
The workaround that has worked for me with IE (and done no harm in FF) is to do something like this:
$('#loading').dialog();
setTimeout(function() {
$.ajax(...);
},1);
That is, show the "loading" message then postpone the Ajax call by use of setTimeout() - this gives the browser a moment to redraw the page after the current JS finishes but before the timeout kicks in.
But of course if you're doing a synchronous request you presumably have additional code that you want to run after the $.ajax() method using its results, so you'd need to move all of that into the function you pass to setTimeout() (or call it from there, anyway).

$(window).unload not working as expected

I'm making a small chat application with PHP + MySQL + JavaScript, I've written a function disonnectUser(), which is called when the user press the disconnect button. Here it is:
function disconnectUser(){
$.post('web/WEB-INF/classes/handleChatUser.php',{ action: 'disconnect',nick: localNickname});
$('#chat').stop(true,true).fadeOut(2000,function(){
nicknameDialog();
});
$('#messageInput').val(null);
$('#clientList').html(null);
$('#chatScreen').html(null);
clearInterval(refreshIntervalId);
clearInterval(refreshIntervalId2);
connected = false;
}
And it works like a charm, but when I call this very function in another context, when the user instead of pressing disconnect just exit the page, in this function
$(window).unload(function() {
if(connected){
disconnectUser();
connected = false;
}
});
it doesn't work. And I'm sure it's being called, because if I put an alert it's called normally before closing the page. I think the page is closing before the code runs completely, so I think if I put some block there until the code finish running it would work?
The problem is that $(window).unload() doesn't waits any AJAX call before closing the window (what is right because AJAX is assync).
You need to force the AJAX to be sync, ie, wait the response. Inside your disconnectUser function:
$.ajax({
type: 'POST',
async: false, // This is the guy.
url: '/blablabla'
});
You can read more about it here: $(window).unload wait for AJAX call to finish before leaving a webpage
Instead of unload, how about beforeunload?
window.onbeforeunload = function() {
if(connected){
disconnectUser();
connected = false;
}
};
Also, your disconnectUser method already sets connected to false, no need to do it here also.
It also seems that jQuery doesn't really handle the beforeunload event, which is why you'll need to revert to native JS to handle this:
http://groups.google.com/group/jquery-en/browse_thread/thread/4e5b25fa1ff5e5ee?pli=1
Try using a synchronous request. Perhaps in combination with onbeforunload like the other poster suggested. If that doesn't work, I suppose you're out of luck. A request that is synchronous blocks the browser while it's happening, so you might want to use it only for the unload function, assuming the method even works.
function disconnectUser(){
jQuery.ajax({
url: 'web/WEB-INF/classes/handleChatUser.php',
data: { action: 'disconnect',nick: localNickname},
type: 'POST',
async: false
});
$('#chat').stop(true,true).fadeOut(2000,function(){
nicknameDialog();
});
$('#messageInput').val(null);
$('#clientList').html(null);
$('#chatScreen').html(null);
clearInterval(refreshIntervalId);
clearInterval(refreshIntervalId2);
connected = false;
}

Categories