This has / may have been asked before, but, as far as I remember, the question was not satisfactory answered at that time ^^
How can I register a window or tab closing event with Javascript? I have tried body.onclose and body.onunload, and dozens others whose names I made up myself and thought they might possibly exist, but none of it worked, or, if it did, it only fired after the window or tab has been closed.
Question:
Is there any way to register such an event before the window or tab has been closed? It needn't even be compatible with all browsers, as long as it works with Firefox.
window.onbeforeunload= …;.
window.onbeforeunload = function (e) {
var e = e || window.event;
// For IE and Firefox
if (e) {
e.returnValue = 'Any string';
}
// For Safari
return 'Any string';
};
WE don't have Onclose but we have OnbeforeUnload. Even I am looking for same event but no luck.
Unload and beforeUnload have negative effects on the "page cache" (also known as the "back forward cache"). This is a special cache that preserves some extra state when you navigate away from the page, so that it can be seamlessly resotred when you navigate 'back' (or unclose a tab). If you've ever wondered why some form fields keep their contents when you click back and some don't - this is often part of the reason.
You should probably take a look at the "pageshow" and "pagehide" events in Firefox and
WebKit. IE doesn't have a page cache equivalent, so in that environment you should be fine to use before/unload. Despite Opera's early concerns, pageshow and pagehide made it into the HTML5 Spec
Related
I have page that is doing the routing on clientside, using history API & push/popstate. Which works just fine on all the modern browsers. (search engines will be supported by node.js prerenderer)
However, I recently bumped into issue where IE doesn't fire popstate on hashchange while, while pushstate with urls works just fine, including IE11.
For example, like so...
$(document).on('click', 'a', function(e) {
e.preventDefault();
History.pushState({}, '', $(this).attr('href'));
});
...which correctly fires...
$(window).on('popstate', function() {
console.log('url changed');
});
According the W3C spec, the hashchange should fire popstate as it's changing the current history. However, when I add in hash links (<a href="#hashchange">...), clicking that on IE, nothing fires. :/
I wouldn't want to do IE detecting (as nowadays there are so many browsers which might fall in to the same pit of doom), rather than using feature detection. However, as history (popstate/pushstate) works just fine the rest of the way I can't even detect the issue on missing push/popstate...
if(!window.history || !window.history.pushState) { ...
... and use the hashchange instead. :/
Any thoughts?
PS. As a bonus, using jquery.history.js (jquery wrapped version of history.js) with hashtag url blows the whole thing up.
http://localhost/routetest/index.html#/page1/1234
becomes
http://localhost/page1/1234
... ??? :/
This is a known issue in IE11 and all Internet Explorer browsers before Edge.
See this link https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/3740423/. The Microsoft response is the last post in this issue report, and notes the newest version that is working.
All versions of IE will display this behavior, and hacking/monkey-patching the correct behavior back into the event framework is probably the only way to make it work reliably. This means you will likely need IE specific logic if you want to implement it yourself.
Try using a polyfill for History API for better support https://github.com/browserstate/history.js
In IE 10 and 11 the popstate event will only be fired for a history item after the state has been set with history.pushState or replaceState, including when it is set to null, and only when traversing between two items that have had state set. Therefore, it is necessary to set the state for a new history item in the hashchange event. Once the state is set, the popstate event will fire when the user navigates through the browser history.
https://developer.mozilla.org/en-US/docs/Web/API/History
The following code
<a data-rel="dialog" href="/Client/Events?ID=c2a7a58e-bef1-4574-987f-4adfc2c3ecc1">
<div class="ui-li-aside"> 1 </div>
<div>
Alejandra Garcia
</div>
</a>
This works on my computer browsers, also works on windows phone browser. But does not work on iphone or ipad browser. Looks like the link gets clicked but i see no dialog. Either it is super delayed or just not working.
The response and load time for the page is pretty quick and works seamless on a regular desktop browser. Its a very small page that loads up.
any tips that i should try to get it to work?
I suspect it has to do with urls you are using and JQM not understanding what to make of links like this:
/Client/Events/StubCheckIn/innasf?stubID=c807321b-5381-4338-adef-9e676374d85d
There is no file ending, no hashtag and I've been fighting with a simple [?para=meter] (https://github.com/jquery/jquery-mobile/issues/4253) link and JQM handling it for a few days.
The easiest way I solve "nothing happening on ipad" is enableing the iPad debugger and console.log-ging through the respective JQM widget. In your case start here (about line 3582):
// click routing - direct to HTTP or Ajax, accordingly
$( document ).bind( "click", function( event ) {
// your first flag
console.log("flag 1 - click detected")
if( !$.mobile.linkBindingEnabled ){
return;
}
var link = findClosestLink( event.target ), $link = $( link ), httpCleanup;
if ( !link || event.which > 1 || !$link.jqmHijackable().length ) {
// your second flag
console.log("flag 2 - not a link?!?)
return;
}
...
Do this all the way through the click-handler and make sure you flag all returns, which end the function for various reasons.
When you are done with the click-handler (not that long) you will see it firing a changePage, so follow it to the changePage handler and continue flagging. This should start around line (#3232) here:
// Show a specific page in the page container.
$.mobile.changePage = function( toPage, options ) {
console.log("flag x - made it to changepage");
...
Go all the way through changepage and then follow to transitionpages (that won't be it I think) and $.mobile.loadPage (about #2931, more likely).
Once you have flagged everything (stop after loadpage! :-), reload the page on browser and ipad and see where the ipad fails vs the browser. Probably will take an hour but you will have a good understanding of how JQM works afterwards :-)
Another reason could be the pushstate handler (about #3781). If a browser supports pushstate (desktop yes, iOS... not sure, iOS3 no) you can make nice URLs, so if you go from your page:
root.com/client/events/checkin/innasf
to a dialog
http://root.com/Client/Events/StubCheckIn/innasf?stubID=96e63aee-1465-4ecd-ad35-123f240d09ff
your browser still displays the page url - probably because you set changehash to false (which keeps the url). This (I believe) is done by the pushstate handler, because your URL should be:
root.com/client/events/checkin/innasf#http://root.com/Client/Events/StubCheckIn/innasf?stubID=96e63aee-1465-4ecd-ad35-123f240d09ff&some-dialog-ending
This will be the url non-pushstate devices will try to resolve (my iPad ios3.3, for example), where it also does not work.
My guess however would be that your links fail somewhere in the click or changepage handler, when the URL is resolved by JQM.
If you flag yourself through, let me know where the difference between browser and iOS shows up. Then we can see if this is fixable.
Ok, I checked the site under the Debug console in iOS Safari. It appears Safari is blocking the cross-domain request to facebook that your dialog loads (referenced here: Unsafe JavaScript attempt to access frame warning in Safari) - that was the warning the debug console showed. From my research, it seems Safari is stricter about this than other browsers.
In my web application, I launch a desktop application using a custom url protocol. For example: "fakeproto://" will launch "fakeproto.exe" on your desktop. If you don't know what I'm talking about, read this: Registering an Application to a URL Protocol
I needed a way, though, to make sure "fakeproto.exe" was installed on the user's PC before attempting to launch. It's a bit of a hack, but I got it to work for all the major browsers. IE presented the most problems and had a unique implementation.
In Javascript, I would first try to load the custom url protocol:
window.open('fakeproto://', '_self');
Before this, I actually defined the following onunload event handler:
window.onunload = function()
{
window.open('help.php', '_self');
}
So if the desktop didn't recognize the custom url protocol, IE would simply leave the current page and go to "webpage cannot be displayed". In this event, the onunload event handler would fire and open help.php instead.
This works great in IE7 & IE8, but once I upgraded to IE9, this no longer works? It goes to "webpage cannot be displayed" instead of help.php.
Using a debugger, the onunload event handler is firing and the code is being executed correctly, but for some reason the window.open call isn't working??? I disabled the pop-up blocker as well, to make sure it wasn't that. No luck.
Anyone have any ideas? Anyone hear of IE9 being more strict with window.open? Anyone know of any alternative solutions to the original problem?
BTW, I can get it to work if I open help.php to a new window.
window.onunload = function()
{
window.open('help.php', '_blank');
}
But this only works if any pop-up blockers are disabled. I would like to avoid using this solution.
Yes, IE9 blocks navigations in the onunload handler.
Does anyone know if the onbeforeunload event is supported on the iPad and/or if there's a different way to use it?
I've tried pretty much everything, and it seems like the onbeforeunload event is never triggered on the iPad (Safari browser).
Specifically, this is what I've tried:
window.onbeforeunload = function(event) { event.returnValue = 'test'; }
window.onbeforeunload = function(event) { return 'test'; }
(both of the above together)
window.onbeforeunload = function(event) { alert('test')'; }
(all of the above functions but inside <body onbeforeunload="...">
All of these work on FF and Safari on the PC, but not on the iPad.
Also, I've done the following just after loading the page:
alert('onbeforeunload' in window);
alert(typeof window.onbeforeunload);
alert(window.onbeforeunload);
Respectively, the results are:
true
object
null
So, the browser does have the property, but for some reason it doesn't get fired.
The ways I try to navigate away from the page are by clicking the back and forward buttons, by doing a google search in the top bar, by changing location in the address bar, and by clicking on a bookmark.
Does anyone have any idea about what's going on? I'd greatly appreciate any input.
Thanks
This bit of JavaScript works for me on Safari and Chrome on ipad and iphone, as well as desktop/laptop/other browsers:
var isOnIOS = navigator.userAgent.match(/iPad/i)|| navigator.userAgent.match(/iPhone/i);
var eventName = isOnIOS ? "pagehide" : "beforeunload";
window.addEventListener(eventName, function (event) {
window.event.cancelBubble = true; // Don't know if this works on iOS but it might!
...
} );
I have found that the onunload() event does fire. It's behavior is somewhat odd; whatever you have in your callback function attached to the event is actually run after the new page has loaded in the background (You can't tell it's loaded yet, but server logging will show that it has).
More oddly, if you have a confirm() call in your onunload(), and the user has clicked a link to go somewhere else, you are in business. If, however, the user closes the iPad Safari browser tab, the onunload() event will fire, but your confirm() will have an implicit cancel as response.
Only Apple would know for sure, but my guess is that they purposely did not enable that functionality in mobile Safari because it is most often used by shady characters to get you to stay on their site or pop up lots of porn/advertising windows.
There's a known bug in WebKit with onbeforeunload. I believe it's fixed in the latest beta of Chrome 5, but it's quite possible the iPad's browser is made from a version of WebKit that doesn't have the fix.
Related Chrome bug report.
beforeunload event is not supported by Mobile Safari. You can see the list of all supported events here: Handling Events Apple documentation
And the beforeunload is not in the list!
https://code.google.com/p/chromium/issues/detail?id=97035
see hear.
alerts are no longer allowed during page dismissal events (beforeunload, unload, pagehide).
I think alerts, prompt, confirm, and other actions like these are also no longer allowed.
Here's a solution that should work on all modern browsers:
var unloaded = false;
window.addEventListener("beforeunload", function(e)
{
if (unloaded)
return;
unloaded = true;
console.log("beforeUnload");
});
window.addEventListener("visibilitychange", function(e)
{
if (document.visibilityState == 'hidden')
{
if (unloaded)
return;
unloaded = true;
console.log("beforeUnload");
}
});
Mobile browsers don't tend to not support beforeunload because the browser can go into the background without unloading the page, then be killed by the operating system at any time.
Most desktop browser contain a bug that causes visibilityState to not get called when the document unloads. See: here.
Therefore, it's important to include both events to cover all scenarios.
NB
I have used console.log instead of alert in my example because alert will get blocked by some browsers when called from beforeunload or visibilitychange.
If you just need to know if the page has been left you can use document.unload. It works fine in ios browsers. If you see on Apple documentation you'll find that it's deprecated and they recommend to use document.pagehide
I'm currently working on a jQuery plugin that tracks a visitors mouse behavior. Movements, clicks, scrolling and resizing are all recorded and sent, via Ajax, to a location where this data is parsed and stored.
Originally, the data is sent to a script when the user leaves the page. By 'leaves' I'm referring to refreshing, going back and forth though history, closing the window/tab and going to a different address.
The solution works in all browsers EXCEPT for Opera. I'm using jQuery's 'unload' event which isn't supported by Opera at all. Neither is onbeforeunload or onunload.
The question is, how do I implement this kind of functionality for Opera browsers?
One solution I had was to make special use of a 'polling' feature I created. This feature allows you to specify an interval which pushes the content to the server every 'x' seconds. Setting this to 1 second specifically for Opera browsers would probably solve this issue, but it's an awful amount of overhead and the requests aren't always completed in sequence, etc ...
Any suggestions or am I only stuck with the above option?
Thanks!
I suppose I could just link you guys to the plugin source. http://www.thedrunkenepic.com/junk/jquery.mousalytics.js
Regarding the code linked above, adding:
if(window.opera)
{
options.interval = 1;
}
On line 89 works great. My only concern is overhead, so I'm still looking for a more elegant solution.
According to http://bytes.com/topic/javascript/insights/799229-browser-quirk-onload-onunload-do-not-fire-back-forward-refresh-opera, Opera never really fires onload / onunload events, so functionality like this isn't possible without hacks.
http://dev.opera.com/articles/view/efficient-javascript/?page=4 seems to confirm this, and basically states that opera tries to maintain the state of the page across requests.
On further investgation, http://unitehowto.com/Onunload indicates that it might be possible with opera.io.webserver.addEventListener('_close', onunload, false); (where onunload is a previously defined function), however it also indicates that this functionality is not consistent across all versions of opera, and might not work at all.
I think that your best option is probably to use the polling option for Opera, or possibly use a server-side check for the current page and where it falls in the history queue.
Does adding this line of JavaScript work for you?
history.navigationMode = 'compatible';
Source: http://www.opera.com/support/kb/view/827/
I've had the same problem and this saved my day:
if( typeof(opera) != 'undefined' )
{
opera.setOverrideHistoryNavigationMode( 'compatible' );
history.navigationMode = 'compatible';
}
More info about this problem can be found at: http://www.opera.com/support/kb/view/827/