I need to make sure that when someone reloads or closes my page, their progress is saved. To save progress, I do a POST via XMLHttpRequest(), sending the data to server.
I'm triggering this saveData() function inside a window.onbeforeunload event.
The problem is, that saveData() does some calculations, and then calls a sendData(content) to finally actually POST the data.
And if the data I'm processing&sending is large (>100kB), the window seems to close before all the data gets through to the server. (I'm sending an image, and some times I only get half of it on the other side)
The http request is synchronous, xhr.open("POST", url, false), but that doesn't seem to cut it.
So my question is, how can I keep the onbeforeunload function from terminating, UNTIL xhr.readyState == 4? How do I make it wait for that event?
Oh, and setTimeout() will not work. The window will close before the "Time" comes.
This won't work:
if (!jQuery) {
setTimeout(attachCode, 100);
return;
} else {
// continue and close
}
Thanks for any insight!
PS: this shouldn't be "bad practice" since it only takes a few of seconds and I don't want the user to have to save manually. Also, saving while he works (as in, before he closes window), would slow down the app so I can't do that.
[EDIT] As a temporary measure, I resorted to using this trick to auto save the data if the user decides to stay on the page. However, I'd really like to not need to use any alert messages. :(
Disallowing the browser to do it's normal behaviour is normally a bad idea. And with onbeforeunload you can't pause the unload, you can only let the user know, that he/she is leaving the page and then let them decide, whether or not to leave the page - and thus the data unsaved.
So i your case, i would suggest an draft autosave, like you see in Google documents + a warning when the user leaves the page with unsaved data.
Actually you can slow down the browser before unloading. The problem lies in how JS handles the ajax requests.
Quite a while ago I had to do a quick and dirty hack about almost the same thing - logging some stuff, before navigating. The only way I found to do it is to wait for the return value of the XHR request.
Even though it's synchronous, the execution of the code forks in background and doesn't actually block the browser. You have to get the return value to be able to halt the script to upload the data.
Mind that I used jQuery for this, but it applies to native JS (I think.. :))
/* some code above */
var request = {
async:false,
cache: false,
url: this.serviceURL,
type: 'POST',
data: {...},
success: function(data) {}
};
try {
var asd = $j.ajax(request); // do the request and wait
}
catch (e) {}
/* some code below */
I hope this helps.
Related
I would have expected, that in the following function the first window.location.href works as a return, gets redirected to example.com, and the rest of the code would be ignored.
() => {
window.location.href = 'http://example.com/'; // Does nothing
console.log('does it log?'); // Yes, it logs
window.location.href = 'http://example.org'; // Redirects here
}
Navigating to another page sounds like a definitive stop: discard DOM, abandon XHR queries, leave the site etc. Why the rest is still executed?
Short answer - it depends on the browser's speed of doing the redirect to the other page, hence unpredictable.
What's happening here is that the browser will try to execute the code after
window.location.href = 'http://example.com/';
Until the redirect occures and the page will go to the next web adress the rest of the code will execute as it supposed to run regular so the number of lines of code that will be executed actually depends on the browser's speed of doing the actual redirecting.
I am designing a HTML page, and I would like to send a simple message (or trigger some action) when the user intentionally request updating (update button on the web browser, pressing F5... or whatever any other manual method that could exist) of the HTML file.
Something like:
window.onmanualupdaterequest = alert("You requested update");
Or whatever the correct procedure could be.
How could I do this?
Further notes:
I have tried the window.onbeforeunload function (example), but it does not exactly solve the problem (I would say it has not the same behavior as user request).
I would like to ignore the autoupdate case (like in setInterval or similar functions or scripts) from the manual update case. This question is about the manual one.
The classical Android swipe-down update method for a web page is considered here as a manual update method.
My idea would be to use the sessionStorage to save the window.location.href on pageload. If the user reloads the page the stored location should match the current url:
window.addEventListener('load', function () {
const lastUrl = sessionStorage.getItem('lastUrl');
if(lastUrl && lastUrl === window.location.href) {
alert("You requested update");
}
sessionStorage.setItem('lastUrl', window.location.href);
});
Is there any way to get the http status of the current web page from javascript?
Spent some time searching on the web, but no luck at all... Seems like it's not possible, but wanted to check with Stack Overflow for maybe some fancy workaround.
(Providing it from the server as part of the response body is not acceptable, the status is supposed to be only available via the http header)
This is not in any way possible, sorry.
Yes You can
Simply request the same page, i.e. URI, using the XMLHttpRequest. Suppose that your page on /stop.php in stop.php you may do something like:
<script>
function xhrRequest(){
console.log(this.status);
// Do some logic here.
}
function getReq(url){
var oReq = new XMLHttpRequest();
oReq.addEventListener("load", xhrRequest);
oReq.open("GET", url);
oReq.send();
}
getReq("/stop.php");
</script>
Checkout this DEMO
🕯 Note:
You have to note that, it is a copy of the page not the page itself.
I, already, have used this solution on a page in which the server may
generate Forbidden HTTP status code when the request is come from
unauthorized IP address, so the condition here is very simple and
there is no much difference between the original and the copy page
that you have simulate its visit.
As a one liner:
fetch(location.href).then(response => console.log(response.status));
This is asynchronous, if you need a synchronous solution use XMLHttpRequest (as in the other answer) together with async: false or use async/await which feels synchronous, but is still asynchronous under the hood.
Alternatively
An approach without an extra call would need to include the status code in the page on the server side (e.g. in a meta tag), then read it on the client side via JavaScript.
Java + Thymeleaf:
<meta name="statuscode" th:content="${#response.status}">
PHP (unverified):
<meta name="statuscode" content="<?php echo http_response_code() ?>">
It is not beautiful, but you can use:
t = jQuery.get(location.href)
.success(function () { console.log(t.status) })
.error(function() { console.log(t.status) });
That When Eric says, this solution will make a new request from the same paga, and not show status of current request.
you can only check status of page loading
try:var x = document.readyState;
The result of x could be:
One of five values:
uninitialized - Has not started loading yet
loading - Is loading
loaded - Has been loaded
interactive - Has loaded enough and the user can interact with it
complete - Fully loaded
Currently I am using message passing to send a request from my contentscript for data in localStorage and I am not having any issues with that, the content script is working as expected.
Can you do this in the other direction?
I have an object that exists in the content script that has a method called ".apply()" and I want to run it when the used clicks the option to do so.
I tried to make a listener in the content script like this:
var myLinker = new Linker();
chrome.extension.onRequest.addListener(function(request) {
if (request.method == "apply")
{
myLinker.apply("nothing");
alert("applied");
}
else
; //Do nothing
And send requests to it like this:
chrome.extension.sendRequest({method: "apply"}, function(){
alert("Tried to request");
});
I get that it is a bit of a hack, but it is the only thing I could think of, and it doesn't even work =/
Is there a way to do this?
I am pretty sure I could just inject new code into the page from the popup (I think I saw an api function for that), and then run stuff, but that would take more memory and just feels like a bad way to do it, because you would basically have the exact same code twice.
To send a message from the extension to a content script, use chrome.tabs.sendMessage instead of chrome.extension.sendRequest.
Because sendRequest has been superseded by onMessage in Chrome 20, there's no official documentation for sendRequest any more. The documentation for chrome.tabs.sendMessage can be found here. Please note that these events cannot be mixed, use either *Request or *Message.
Yes, you would use this: http://developer.chrome.com/extensions/tabs.html#method-sendMessage
Content scripts live within the DOM of the page. And each page that is open within Chrome has a tab ID associated with it -- http://developer.chrome.com/extensions/tabs.html#type-tabs.Tab
Let's say you want to send the {method: "apply"} to a page that was just opened in a new tab:
chrome.tabs.onCreated.addListener(function(tab) {
chrome.tabs.sendMessage(tab.id, { method: "apply" });
});
There are other events/methods to get the specific Tab you want to send the message to. I think there's one called getCurrent to send to the currently selected tab, check out the docs for that.
This question already has answers here:
JavaScript, browsers, window close - send an AJAX request or run a script on window closing
(9 answers)
Closed 6 years ago.
Basically, once a user leaves a webpage in my application, I need to call a PHP script with AJAX, which will insert a time spent on the webpage to the database and then leave the page.
It is important to wait for the AJAX request to finish because webpages in my application are not accessible to users unless they have spent a certain time on a previous page (let's say two minutes).
Here is my jquery code:
$(document).ready(function() {
var teid = TEID;
var startTime = new Date().getTime();
$(window).unload(function() {
var timeSpentMilliseconds = new Date().getTime() - startTime;
var t = timeSpentMilliseconds / 1000 / 60;
$.ajax({
type: 'POST',
url: '/clientarea/utils/record-time',
data: 'teid=' + teid + '&t=' + t
});
});
});
How should I change it so it will wait for the AJAX request to end before leaving the webpage?
EDIT:
Or it might be better (easier) to just let the AJAX request be repeated every minute or so. Is that possible?
Well, you can set async: false on your AJAX call to make the browser wait for the request to finish before doing anything else, but note that this will 'hang' the browser for the duration of the request.
$.ajax({
type: 'POST',
async: false,
url: '/clientarea/utils/record-time',
data: 'teid=' + teid + '&t=' + t
});
From the manual:
By default, all requests are sent asynchronous (i.e. this is set to true by default). If you need synchronous requests, set this option to false. Cross-domain requests and dataType: "jsonp" requests do not support synchronous operation. Note that synchronous requests may temporarily lock the browser, disabling any actions while the request is active.
âš WARNING: This answer was posted in 2010 and is now outdated - the XHR specification highlights the following statement:
Synchronous XMLHttpRequest outside of workers is in the process of being removed from the web platform as it has detrimental effects to the end user’s experience. (This is a long process that takes many years.) Developers must not pass false for the async argument when current global object is a Window object. User agents are strongly encouraged to warn about such usage in developer tools and may experiment with throwing an "InvalidAccessError" DOMException when it occurs.
DevTools in Chrome has recently started warning about it, so this change (which has been coming for some years) could be imminent.
The best solution is to use navigator.sendBeacon. It is brand new functionality which is starting to get implemented in new releases of browsers. The function is available in browsers newer than Chrome 39 and Firefox 31. It is not supported by Internet Explorer and Safari at the time of writing. To make sure your request gets send in the browsers that don't support the new functionality yet, you can use this solution:
var navigator.sendBeacon = navigator.sendBeacon || function (url, data) {
var client = new XMLHttpRequest();
client.open("POST", url, false); // third parameter indicates sync xhr
client.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
client.send(data);
};
Hope this helps!
How about setting a cookie in the unload handler? The server should see it on the subsequent requests.
<script>
$(window).unload(function(){document.cookie='left_on='+(new Date())})
</script>
for me, yours is not a good idea for the browser to wait before closing...
simply because what if I really want to close it?...
if a page bother a user, it's not good...
my suggestion is, in the page, you wait for 2 minutes (if 2 minutes is the requirements), then send an ajax that the user has done his 2 minutes...
you can then check it on the server side if one has his 2 minutes or not...
It is a bad idea to try and hijack your users' browser, since it will give them a bad feeling and send them away.
If for some reason you want not to produce a new page until the user has spent a minimum time on the previous one, the best thing to do is to pilot server side, i.e. redirecting to the current page until the requested time has passed.
You don't even need to make ajax calls, just store in the session the timestamp of when the page was served, and don't show the following page until a certain amount of time has passed.
Be sure to tell the users they have to wait for a new page to be ready, maybe with a simple javascript countdown.
If you want the user to actually have the page active for a certain amount of time (i.e. not to switch to another tab/window waiting for the two minutes to elapse), well, I cannot propose an effective solution.
use onbeforeunload:
$(document).ready(function(){
window.onbeforeunload = function(){
// $.ajax stuff here
return false;
}
});
This will at least bring the user a messagebox which asks him if he wants to close the current window/tab.
I think it would be much better to use a polling technique as you suggest, though it will cause some load on the web server.
$(document).ready(function() {
var teid = TEID;
var startTime = new Date().getTime();
var ajaxFunc = function() {
var timeSpentMilliseconds = new Date().getTime() - startTime;
var t = timeSpentMilliseconds / 1000 / 60;
$.ajax({
type: 'POST',
url: '/clientarea/utils/record-time',
data: 'teid=' + teid + '&t=' + t
});
};
setInterval(ajaxFunc, 60000);
})
You'll be glad when you can use websockets :)
The jQuery.ajax() method has the option async. If you set it to false the call will block until the response comes back (or it timed out). I'm pretty shure, that calling this, will yause the browser to weit in the unload handler.
Side note: You can't rely on this to work. If the browser gives the user the option to cancel the unload handlers (which some browsers do after a while of waiting), the "time spend on site" will never be updated. You could add a timer to the site, which periodically calls a script on the server and which updates the time. You won't have an accurate value, but in your case, this isn't needed.
If you only need to know if the user was X seconds on the page You could simply set a timeout in the onload handler (using setTimeout(function, ms)) which makes a call if the user has spend the needed time. So there would be no need for a unload handler.