This question already has answers here:
Can the unload Event be Used to Reliably fire ajax Request?
(5 answers)
Closed 4 years ago.
I'm creating a queue-like page where only 1 person should be able to edit information about a specific database entry at a time.
Let's say a user is on page profile.php?id=1 - only the very first person who loaded this page should be able to make changes to it.
I've attempted to do this with JavaScript/jQuery, but am facing a problem.
On page load, I send an AJAX request to a PHP file that essentially sets a table cell to true for a specific ID. Then, I use a beforeunload event to set this cell to false when the page is unloaded. With this specific implementation, there is a problem though. The beforeunload event is either not fired when the tab is closed, or it is fired and the AJAX request does not have time to complete. This is a big problem because a user could potentially load up the page, see there is nothing to edit, and then close the tab without thinking about it, thus locking the page indefinitely until I reverse the lock in the database manually - and I would hate to have to force people to not use their browser the way they want to.
Here is my code:
$( document ).ready(function() {
var locked = <?=json_encode($data['in-review']);?>;
if(locked) {
$('input').attr('disabled', true);
$('#delete-model-button').attr('disabled', true);
$('#uidfield').attr('disabled', true);
$('#submitbutton').attr('disabled', true).text("This model is in review by another reviewer.");
} else {
$.ajax({
url: 'process/in-review.php',
type: 'POST',
data: {uid: $("#uidfield").val(), value: 1},
success: function (result) {
console.log("Model Locked in Review");
}
});
$(window).on('beforeunload', function() {
$.ajax({
url: 'process/in-review.php',
type: 'POST',
data: {uid: $("#uidfield").val(), value: 0},
success: function (result) {
console.log("Model Unlocked");
}
});
});
}
});
As you can see, if a user loads into a page that is already review locked, the beforeunload process is not called at all because that would allow users to unlock a form that another user may be working on.
How can I effectively unlock these pages if a user closes their tab or browser?
In case it matters, here is my in-review.php page:
<?php
include('db.php');
$uid = $_POST['uid'];
$value = $_POST['value'];
$db->run("UPDATE `devices` SET `in-review`=? WHERE `uid`=?", [$value, $uid]);
--EDIT:
This is commented as a duplicate question, but I disagree with this. That question is specifically asking "Can the unload Event be Used to Reliably fire ajax Request?" - I already know the answer to that is "Yes", because that is the method I am currently using, I also know it does not work at all for closing tabs/browser, therefore my question is not if this is a reliable method for what I want to do (I already know it is not), I'm asking for a different method that I can use to do this.
have you tried making a synchronous call of Ajax in beforeunload event?
i.e.
$(window).on('beforeunload', function() {
$.ajax({
url: 'process/in-review.php',
type: 'POST',
async: false,
data: {uid: $("#uidfield").val(), value: 0},
success: function (result) {
console.log("Model Unlocked");
}
});
});
The problem here is browser needs to keep connect alive with the server until the request is completed.
When the user clicks on other pages in your application that means connection to the server is alive and operation can be completed, and in case of browser/tab close connection to server is lost.
This might work on some situations i.e. your server was fast enough to respond before complete browser closure i.e. destruction in window object
Related
I'm running this code on blur() on an input. It works great, as long as you don't click on anything that takes you to a different page or closes the lightbox (div in an overlay) where the ajax function and input was loaded from, since there is a chance that the ajax script will be aborted. I get that it may be hard to make sure that the content is saved on blur() if you close the browser window, but is there any way I can wait until the ajax request (and any link click) is finished before I close the lightbox (or move on to a different page)?
I guess another option would be to have on input() instead/as well as on blur(), but then I get a lot of ajax requests and need to start throttling right? My lightbox plugin (fancybox) has an event called beforeClose, can I run something in there that checks if the ajax request was sent?
Please let me know if I'm thinking about this the wrong way. I know that async false is not really recommended, but maybe what I need?
$.ajax({
type: "POST",
dataType: "json",
url: "?a=ajax",
data: { value: $self.val() },
error: handleError
}).done(function(data) {
if( data == "0" )
{
// no success
}
else
{
// success
}
});
I am trying to make.. "something" to know if an user is closing the browser or in other case is idle. I need to logout the user using a token.
I am using Symfony2.4 and actual logout include a handler because I need to know if the person is logged in another computer. I can't simple use a session expire because that handler will not execute and the application will still show the user as logged.
I am using this code and works really good!!
<script>
var unloaded = false;
$(window).on('beforeunload', unload);
$(window).on('unload', unload);
function unload(){
if(!unloaded){
$('body').css('cursor','wait');
$.ajax({
type: 'get',
async: false,
url: "{{ logout_url('main') }}",
success:function(){
unloaded = true;
$('body').css('cursor','default');
},
timeout: 50
});
}
}
</script>
The thing with this is that when the user try to go any link in the website this code execute and they need to log in again.
What can I do to avoid this code run by simply going to another link or what other thing can I do to have similar results?
This is more of a Javascript question, but well.
What about wrapping all your external links with javascript that sets a flag that disables your unload() function ? This is not very smart to do, though, but should work.
Something like that (not tested) :
$('a').click(function(){
// set the flag
myFlag = true;
return true;
}):
and in your unload :
function unload(){
if (myFlag) {
myFlag = false
return
}
// the rest here
}
EDIT :
If you want to send a request on page unload, it's better to use navigator.sendBeacon() as the data is transmitted asynchronously to the web server when the User Agent has an opportunity to do so, without delaying the unload or affecting the performance of the next navigation. See https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon
I was reading this question: Trying to detect browser close event
But it's not comprehensive enough, or at least I'm not sure it is, I need to detect when the user leaves the website, this could be:
internet died
PC shuts down (power outage for example)
user close the browser
user close the browser tab in which the website was running
I'm sure there are more cases.
This probably needs to be managed by the server, and not some javascript event that is not going to be fired in extreme cases.
Any ideas what could be used in this case?.
You could use socket.io and listen for when the socket is lost, or you could have your site send a heartbeat to the server and if X milliseconds goes by without a pulse, you can assume the user left for any of the reasons you listed.
I am using Java Script to detect the event of leaving and sending an ajax request to my server so I can store that the user left the page.
The second part is to detect the time the user is in my page, every 3 seconds I also send a ajax request to my database to save the time of the user in the page.
<script>
window.onbeforeunload = function(){//If user left the page
user_left_page();
};
function user_left_page(){//USER LEFT THE PAGE send data to php to store into my database
var action = "left_page";
$.ajax({
url:"includes/track-page-time.inc.php",
method:"POST",
data:{ action: action},
success: function(data){
},
});
}
//This one below is to store the time the user is spending in your page. I am using both
// in my code, basically I keep storing the data every 3 seconds to know the time
setInterval(function () {
update_user_activity();
}, 3000);//Interval time to send the info to the database
function update_user_activity(){//IS USER IN THE PAGE?
var action = "update_time";
$.ajax({
url:"includes/track-page-time.inc.php",
method:"POST",
data:{ action: action},
success: function(data){
},
});
}
</script>
Another simple method you can track the IP/Session Id and save in the Database, you may update the time in the db using the ajax call in an interval i.e every 5 or 10 minutes.
if user not taken any activity and the time will not be updated, if time in db is less than the time() - $intervals , then you can assume that the user has left, lost connectivity etc.
You could use the window.onbeforeunload for the last two cases i.e. detecting the browser close or tab close event. For the first two events that is power failure or connectivity problems, only continuously ping or like AlienWebguy said the heartbeat mechanism can be implemented.
window.onbeforeunload = confirmExit;
function confirmExit()
{
return "You have attempted to leave this page. If you have made any changes to the fields without clicking the Save button, your changes will be lost. Are you sure you want to exit this page?";
}
Ref site
I want only execute my ajax post 1 time, i try to avoid to the user refresh the page and execute so much times the ajax post,
I thought in create a cookies, but i don't know, and i'm no sure, somebody know how?
This is my jquery.
var t = jQuery.noConflict();
t( document ).ready(function() {
t.cookie("example", "foo", { expires: 7 }); // Sample 2
console.log( "ready!" );
alert(t.cookie("example"));
var data = '<?php echo json_encode($json_full);?>';
t.ajax({
url: 'my api url',
type: 'POST',
success: function(r) { alert(JSON.stringify(r)) },
dataType: 'JSON',
data: { data: data, }
})
});
/I need run this AJAX only one time because is a checkout page to send the order, and if i refresh the page, send every time the same order, and this i don't want/
Thanks a lot!
Things like these can not be safely controlled on the client's browser. Any user with minimal knowledge in JavaScript will be able to open up the developers tools for their browser and manipulate the code or any values you might have stored (such as deleting the cookie you have set).
This limitation should be implemented on the server.
It really depends on the scope of your application. You might be able to limit the requests per IP address, but that might prevent multiple people from the same office for example loading the page at the same time.
Using user authentication and persistent server storage you'll be able to limit the effect of the request, but you probably won't be able to prevent the actual request from being sent as anyone can make that request even from outside the browser. You could store the user_id of the user that initiated the request and only allow the resulting action to occur if a certain time has passed since the last request.
A better solution to avoid double submits, is to use a POST query for the submit request and let the server respond with a redirect to a normal (harmless) receipt/thankyou page.
Then if the user refreshes the receipt page they will simply repeat the GET request to the receipt page and not the post.
You should still add some checks server side to avoid multiple POST requests somehow (using sessions, timestamps or something), in case a malicious user deliberately tries to resubmit.
This will only work on IE8 and above, but you can use localStorage:
var t = jQuery.noConflict();
t( document ).ready(function() {
t.cookie("example", "foo", { expires: 7 }); // Sample 2
console.log( "ready!" );
alert(t.cookie("example"));
if(localStorage['submitted'] === undefined){
var data = '<?php echo json_encode($json_full);?>';
t.ajax({
url: 'my api url',
type: 'POST',
success: function(r) {
localStorage['submitted'] = true;
alert(JSON.stringify(r));
},
dataType: 'JSON',
data: { data: data, }
})
}
});
This way the first time it will run the AJAX because you haven't set the localStorage variable, but upon success you do and it will not resubmit on page refresh.
If you wanted to have the ability to send again upon a future visit, just use sessionStorage instead of localStorage. Same syntax and everything.
I'm working on a chat and I'm trying to figure out how I can detect that the user has left the page or not. Almost everything is being handled by the database to avoid the front end from messing up.
So what I'm trying to do is once the page is left for any reason (window closed, going to another page, clicking a link, etc.) an ajax call will be fired before a person leaves so I can update the database.
This is what I've tried:
$(window).unload(function(){
$.post("script.php",{key_leave:"289583002"});
});
For some odd reason, it wouldn't work, and I've checked the php code, and it works fine. Any suggestions?
Try this:
$(window).unload(function(){
$.ajax({
type: 'POST',
url: 'script.php',
async:false,
data: {key_leave:"289583002"}
});
});
Note the async:false, that way the browser waits for the request to finish.
Using $.post is asynchronous, so the request may not be quick enough before the browser stops executing the script.
This isn't the correct way of doing this... Suppose the OS just hangs or something happens in the browsers process then this event wont be fired. And you will never ever know when the user has left, showing him/her online ever after he/she has disconnected. Instead of this, what you can do is.
Try connecting a socket so that you can know the user is disconnected when the socket is disconnected
You can send a request to the server (say after every 1 sec) so that you can know that the user is still connected. If you didn't receive the request - even after 2 secconds - disconnect the user.
Try to add popup (prompt("leaving so early?")) after $.post. It may work. Tho it may be bad user experience. :)
This is related to the answer above. https://stackoverflow.com/a/10272651/1306144
This will execute the ajax call every 1 sec. (1000)
function callEveryOneSec() {
$jx.ajax({}); // your ajax call
}
setInterval(callEveryOneSec, 1000);
The unload event is not recommended to detect users leaving the page. From MDN:
Developers should avoid using the unload event ... Especially on mobile, the unload event is not reliably fired.
Instead, use the visibilitychange event on document and/or the pagehide event on window (see links for details). For example:
document.addEventListener('visibilitychange', function() {
if (document.visibilityState === 'hidden') {
$.ajax({
type: 'POST',
url: 'script.php',
async:false,
data: {key_leave: "289583002"}
});
}
});
Better yet, use Navigator.sendBeacon, which is specifically designed for the purpose of sending a small amount of analytics data to a server:
document.addEventListener('visibilitychange', function() {
if (document.visibilityState === 'hidden') {
navigator.sendBeacon('script.php', {key_leave: "289583002"});
}
});