I have an iPhone webapp that uses a cache manifest to work offline and add the webapp to my Home screen. What I would like to find is a way to detect that the app is exiting so I can do some housekeeping and save some data.
If I am running as a web page in Safari, window.onunload does this for me, but this event does not happen when running from the Home screen.
I tested the pagehide event using the below code and found that it works well for detecting whether the user navigated to another link or simply opened a new tab when you are in safari.
However, if you are in a web app saved to your homescreen (like you describe) then the pagehide event is useless for telling if the web app was closed.
Depending on what you need specifically, you can work around this limitation by saving data to localStorage and then checking the localStorage when the app opens again. Then you can perform any extra work that may need to be done before the app fully starts again.
function myLoadHandler(evt)
{
if (evt.persisted) {
alert('user returns to page from another tab');
return;
}
alert('loading new page');
}
function myUnloadHandler(evt)
{
if (evt.persisted) {
alert('user goes to new tab');
return;
}
alert('user truly leaves the page');
}
if ("onpagehide" in window) {
window.addEventListener("pageshow", myLoadHandler, false);
window.addEventListener("pagehide", myUnloadHandler, false);
} else {
window.addEventListener("load", myLoadHandler, false);
window.addEventListener("unload", myUnloadHandler, false);
}
I have an answer. It's not what we were hoping for.
The technical definition of unload from http://www.w3.org/TR/DOM-Level-2-Events/events.html is:
The unload event occurs when the DOM
implementation removes a document from
a window or frame. This event is valid
for BODY and FRAMESET elements.
I also got a reply from some in the know at Apple: "Unfortunately, I haven't been able to find an event that fires when the Home button is clicked! I think the most appropriate event for this might be the pagehide event. This event actually does fire in Safari on iOS when you switch to the "tabs" view to open a new page, but not when you click the home screen icon :-("
In iOS3 or multitasking unsupported devices/app, you can do the housekeeping in applicationDidTerminate method.
In iOS4 and multitasking supported devices/app, you can do the housekeeping in applicationDidEnterBackground method. It's best to implement the applicationDidTerminate method as well just in case iOS decide to remove your app from memory, applicationDidTerminate will be called in your app.
Cheers.
Related
I'm sure I'm missing something relatively simple - but for the life of me I can't find the answer. When trying to do a reload prevention in react, my onbeforeunload function is not firing when I press the reload button in the browser menu (Chrome). It works if I press CMD+R/F5 and once that's done once - the browser button also fires the function. It simply doesn't work if I attempt to click reload first. Additionally, if I navigate through the router once, it also seems to register. I am using the following code to register the refresh on my top level template:
class Template extends React.Component {
constructor(props) {
super(props);
}
refreshPrevent = (e) => {
e.preventDefault();
e.returnValue = true;
}
componentDidMount() {
console.log("registering refresh handlers");
window.addEventListener("beforeunload", this.refreshPrevent );
}
componentWillUnmount() {
window.removeEventListener("beforeunload", this.refreshPrevent);
}
Any ideas would be appreciated.
I don't see any errors in your code. I use similar code in my react applications, but I hadn't tested this particular problem before until you posted.
It seems, according to the MDN documentation that this behavior is normal:
To combat unwanted pop-ups, some browsers don't display prompts
created in beforeunload event handlers unless the page has been
interacted with. Moreover, some don't display them at all.
So, when your app first loads, until you interact with the page in some way, you can hit the browser refresh button and depending on the browser, the page will reload without displaying a prompt.
However, once you have done something on the page with your mouse or keyboard or via touch, then the prompt will be displayed.
The MDN documentation concludes (emphasis added):
Note also, that various browsers ignore the result of the event and do not ask the user for confirmation at all. In such cases, the document will always be unloaded automatically. Firefox has a switch named dom.disable_beforeunload in about:config to enable this behaviour. As of Chrome 60, the confirmation will be skipped if the user has not performed a gesture in the frame or page since it was loaded.
This fits with the spec that we should expect certain situations, like an uninteracted-with page plus refresh, to skip prompting the user:
The user agent is encouraged to avoid asking the user for confirmation if it judges that doing so would be annoying, deceptive, or pointless. A simple heuristic might be that if the user has not interacted with the document, the user agent would not ask for confirmation before unloading it.
I'm playing with window.onpopstate, and there is a thing that annoys me:
Browsers tend to handle the popstate event differently on page load.
Chrome and Safari always emit a popstate event on page load, but
Firefox doesn't.
source
I tested it, and yeah, in Chrome and Safari 5.1+ the popstate event is fired on page load, but not in Firefox or IE10.
The problem is, that I want to listen only to popstate events where user clicked the back or forward button (or the history was changed via javascript), but don't want to do anything on pageload.
In other words, I want to differentiate the popstate event from page load from the other popstate events.
This is what I tried so far (I'm using jQuery):
$(function() {
console.log('document ready');
setTimeout(function() {
window.onpopstate = function(event) {
// Do something here
}, 10);
});
Basically I'm try to bind my listener function to popstate late enough to be not bound on page load, only later.
This seems to work; however, I don't like this solution. How can I be sure that the timeout chosen for setTimeout is big enough, but not too big (because I don't want it to wait too much).
I hope for a smarter solution!
Check for boolean truth of event.state in popstate event handler:
window.addEventListener('popstate', function(event) {
if (event.state) {
alert('!');
}
}, false);
To ensure this will work, always specify a non-null state argument when calling history.pushState() or history.replaceState(). Also, consider using a wrapper library like History.js that provides consistent behavior across browsers.
I had a similar problem and i had to validate to make sure if page was loaded completely.
I used something like this :
var page_loaded = false;
window.onpopstate = function(event){
if(!page_loaded){
page_loaded = true;
return false;
}
//Continue With Your Code
}
To react on popstate event, you need to push some state onto the session history.
For example add this line to the document ready section:
history.pushState(null, null, window.location.pathname);
Not ideal, but it works in Chrome, Firefox and other browsers as well.
Then the event is fired correctly when user clicks on Back or Forward button, also when history.back(), history.forward(), history.go() methods are called manually. Each time when popstate has been invoked, you have to push another state again to make it working.
See also:
Single-Page Apps and HTML5 pushState
How to Detect Browser Back Button event - Cross Browser
It seems none of the browsers are emitting the event on page load any more as of today:
Browsers used to handle the popstate event differently on page load, but now they behave the same. Firefox never emitted a popstate event on page load. Chrome did until version 34, while Safari did until version 10.0.
Source: https://developer.mozilla.org/en-US/docs/Web/API/PopStateEvent
This question already has answers here:
Prevent safari loading from cache when back button is clicked
(8 answers)
Closed 4 years ago.
The issue I've found is very similar to this question, except that Safari on desktops seems to have resolved the issue. Essentially, the issue is this: when a client is browsing on mobile safari and the page executes a javascript function on pageA.html, then navigate to pageB.html, then press the back button to go back to pageA.html, the javascript function won't run when the client pressed the back button to come back to pageA.html. It will skip the javascript call.
I've tried the solutions mentioned in the link above, but nothing seems to work for mobile Safari. Has anyone else encountered this bug? How did you handle it?
This is caused by back-forward cache. It is supposed to save complete state of page when user navigates away. When user navigates back with back button page can be loaded from cache very quickly. This is different from normal cache which only caches HTML code.
When page is loaded for bfcache onload event wont be triggered. Instead you can check the persisted property of the onpageshow event. It is set to false on initial page load. When page is loaded from bfcache it is set to true.
window.onpageshow = function(event) {
if (event.persisted) {
alert("From back / forward cache.");
}
};
For some reason jQuery does not have this property in the event. You can find it from original event though.
$(window).bind("pageshow", function(event) {
if (event.originalEvent.persisted) {
alert("From back / forward cache.");
}
});
Quick solution to these problem is to reload the page when back button is pressed. This however nullifies any positive effect back / forward cache would give.
window.onpageshow = function(event) {
if (event.persisted) {
window.location.reload()
}
};
As a sidenote, you can see lot of pages offering using empty onunload handler as solution. This has not worked since iOS5.
$(window).bind("unload", function() { });
I am developing a HTML5 web-application and compiling it with Cordova (phonegap) 1.7.
I want to override the Android backbutton so that I can call window.history.back() instead of closing the application (default Android). How can I prevent Android from killing the defaultactivity on back button pressed?
I get the "Back button pressed!!!!" in logcat, so the method is fired before the application is closed.
This is what I have so far:
// Wait for Cordova to load
//
document.addEventListener("deviceready", onDeviceReady, false);
// Cordova is ready
//
function onDeviceReady() {
document.addEventListener("backbutton", function(e) {
console.log("Back button pressed!!!!");
window.history.back();
}, false);
}
EDIT: I am willing to accept an answer explaining a way to simulate the window.history.back() directly from the DefaultActivity.java android class if that is possible!
I solved my own question by adding the code below to the DefaultActivity.java file to prevent the default android behavior, and keeping the JavaScript code as stated in the question:
#Override
public void onBackPressed() {
return;
}
I hope this helps someone in the future with the same problem!
I took this approach. I hooked the backbutton event as you have shown. I look to see if this is the first page or not and then ask the user if they want to exit the program or not. This depends on what you want your program to do depending on its state. I did not add the override as you have shown; I didnot seem to need it.
if ($.mobile.activePage.attr('id') === 'firstpage') {
// Prompt to confirm the exit
} else {
window.history.back();
}
If they want to exit you can call:
navigator.app.exitApp();
to close your program.
I imagine you still want to allow the user to exit your app. I don't tend to use apps that
do not allow an exit of some kind.
Hope this helps you out.
Never had to do that but, have you tried to return true ?
Like in the Java SDK, if you return True, the system will assume you have correctly catched the event and will no longer pass it to other event listeners.
Is there a way by which we can capture the click of HOME and BACK button in the html file in android application using phonegap/jqtouch/javascript?
I have an application for Android using phonegap. I want to capture the click of native HOME and BACK button of the Android phone in the html page to exit / go back gracefully.
You can catch the BACK button event in PhoneGap, however not the HOME button (this is a bad Android practice as there is a clear user expectation regardless of the app you're using about what the HOME key does: sends you back to your home screen! You don't want to override this functionality).
I will direct you to pieces of code in PhoneGap (LATEST source! pull from github for latest version of the phonegap framework) for guidance.
First, there is a 'BrowserKey' java object bound to the 'BackButton' JavaScript global:
http://github.com/phonegap/phonegap-android/blob/master/framework/src/com/phonegap/DroidGap.java#L291
The definition of this class is here: http://github.com/phonegap/phonegap-android/blob/master/framework/src/com/phonegap/BrowserKey.java
First thing you need to do in your application (I suggest you run this during application initialization) is to let the native side of the framework know you are overriding BACK button functionality. You would do this in JavaScript with a simple call:
BackButton.override();
From there on out, you can attach an event handler to the document's 'backKeyDown' event to execute logic every time the BACK button is hit. Something like this should work:
document.addEventListener('backKeyDown', function(e) {
alert('you hit the back key!');
}, false);
As an addendum, here is the JavaScript code that wraps the back button event dispatching: http://github.com/phonegap/phonegap-android/blob/master/framework/assets/js/keyevent.js
Basically, after calling BackButton.override(), the native side of the framework will call window.keyEvent.backTrigger() every time the BACK button is hit.
This code sample works for PhoneGap 0.9.5 and later (tested on 0.9.6) :
document.addEventListener("menubutton", function () {
alert('Menu button');
}, false);
document.addEventListener("searchbutton", function () {
alert('Search button');
}, false);
document.addEventListener("backbutton", function () {
alert('Back button');
}, false);
Home button can't be handled. That's reserved by the system.
I have an application for Android using phonegap. I want to capture the click of native HOME and BACK button of the Android phone in the html page to exit/go back gracefully.