javascript that executes when leaving page - updating legacy code - javascript

Just a bit of background - I was updating a legacy feature that is all javascript as there was new functionality being added to our web app that the legacy feature had to interact with.
While testing this update we noticed that there was an issue when moving from page to page within the web app - the legacy feature is supposed to follow you if it has been activated.
Basically the data from the activated feature wasn't being retained. There is some code in there that was supposed to do this. After some investigation it was discovered that it is working as expected in IE/Edge and Firefox but the exit code is not getting executed in Google Chrome and Safari.
This might explain why the business wasn't aware of the issue but it's been confirmed that the issue is also in the live site so hasn't been recently introduced as I believe most of the users would be on IE.
The code that was managing the exit functionality was as follows:
if(window.onpagehide || window.onpagehide === null){
window.addEventListener("pagehide", storeDataFunction, false);
} else {
window.addEventListener("unload", storeDataFunction, false);
}
With the above code the logic within the storeDataFunction doesn't even execute when you attempt to leave the page.
After some browsing on here and other forums I have updated the above to:
window.onbeforeunload = storeDataFunction;
and made storeDataFunction return null.
This appears to be slightly better in that the storeDataFunction is getting executed when moving pages but deep within it there is an ajax POST to a controller in our web app that stores the data necessary to check if the feature is active and what it has done and this ajax request does not get executed.
Again this is only on Chrome and Safari and works fine on IE and Firefox.
So what I'm looking for is a steer. Am I heading down the wrong path using onbeforeunload for what we want to achieve here? Or was the original use of pagehide/unload more appropriate?
Are both of those incorrect and there is a better way of doing it?
This feature was developed about 7 years ago so I'm not even sure if it ever worked on Chrome or if it did at time of development and some more recent updates in Chrome changed they way it handles page termination.
Thanks in advance.

Looks like Google have updated Chrome to stop XHR during page dismissal.
https://www.chromestatus.com/feature/4664843055398912

Related

Javascript redirect not working in Edge browser when opened with android ActionView intent, but working after manual reload

Situation
In our Android app (Xamarin), we open a web page using an ActionView intent. The code looks like this:
Intent intent = new Intent((string)Intent.ActionView, Android.Net.Uri.Parse(args.url));
intent.AddFlags(ActivityFlags.NewTask);
The opened page at some point does a JS redirect, with a line like this:
window.location = '...';
We tried many different variations of that line, including window.location.href = '...', window.location.assign('...'); and some more. All show the same behavior.
Problem
This has worked fine for years now, in all browsers - but now we ran into a problem, when the browser on the android device is the Edge browser:
When the browser tab is initially opened by the intent, the window.location = '...' line in Javascript is just ignored by the browser. No error message - just ignored.
However, if that same browser tab with exactly the same URL is opened manually (either by reloading or by copying and pasting the URL), the JS redirect is executed just fine.
Question
How do we fix this, how do we make the JS redirect reliably work?
My guess is that we are running into a security feature, which prevents JS redirects in browser tabs that the user has never interacted with.
Is there something (maybe an intent flag?) to circumvent this? We already tried the flag GrantWriteUriPermission, but it did not help.
Possible duplicates
Android Browser Facebook Redirect Does Not Always Trigger Intent for URL :
The proposed situation of setting the URL on a link and faking a click on it did not work.
Microsoft Edge security
Microsoft Edge recently fixed an issue regarding XSS Targeting Non-Script Elements (June 24, 2021).
The vulnerability was found by two researcher when they visited a website in another language via the Microsoft Edge browser and attempted to translate the page. The goal of the recent fix by Microsoft is to avoid vulnerability regarding accessing dynamically to a content from a third party application and specifically in the case of browser redirection. They need to act quickly because the vulnerability is quite huge.
In order to mitigate a large class of potential cross-site scripting issues, the Microsoft Edge Extension system has incorporated the general concept of Content Security Policy (CSP)
Ok, but ... is there any solution?
Maybe you can find a solution to solve your issue here, in particular the part concerning the <button onclick="...">.
Inline code is considered harmful in concept of CSP and microsoft recommend some good practices :
1 - The clickHandler definition must be moved into an external JavaScript
2 - The inline event handler definitions must be rewritten in terms of addEventListener and extracted into your external js file. If you are currently starting your program using code like <body onload="main();">, consider replacing it by hooking into the DOMContentLoaded event of the document, or the load event of the window, depending on your requirements. Use the former, since it generally triggers more quickly.
3 - Function inside onclick call must be rewritten to avoid converting the string of function into JavaScript for running.
The code exemple of the external .js file cited in the documentation look like this :
function awesome() {
// Do something awesome!
}
function totallyAwesome() {
// do something TOTALLY awesome!
}
function awesomeTask() {
awesome();
totallyAwesome();
}
function clickHandler(e) {
setTimeout(awesomeTask, 1000);
}
function main() {
// Initialization work goes here.
}
// Add event listeners once the DOM has fully loaded by listening for the
// `DOMContentLoaded` event on the document, and adding your listeners to
// specific elements when it triggers.
document.addEventListener('DOMContentLoaded', function () {
document.querySelector('button').addEventListener('click',
clickHandler);
main();
});
Hope it's helps

Using startTrackPage in Application Insights

I have recently been messing around with Application Insights and have been having a problem with getting the JavaScript API to work. In the default script that you are supposed to add to your page, they use the function trackPageView(). This seems to works, but I had also wanted to gather information about how long a user stayed on the page. I found the startTrackPage() and stopTrackPage() functions and tried to use those to get the information, but I always receive an error from startTrackPage().
Uncaught TypeError: appInsights.startTrackPage is not a function(anonymous function)
I have stepped through the code and the function does not seem to be created at the point I am calling it. trackPageView() is already defined though. I tried calling the function after the document loaded as well and that still failed. However, I can call it from the developer console once the page loads.
Here is where I found the information about startTrackPage() and stopTrackPage() originally. Other than that I have just been perusing around to see if anyone else has encountered this.
I appreciate the help.
Default code from App Insight:
window.appInsights = appInsights;
appInsights.startPageView();
What I have tried:
window.appInsights = appInsights;
appInsights.startTrackPage();
window.onunload = function () {
appInsights.stopTrackPage();
};
Short answer: unfortunately there's currently no way to use appInsights.startTrackPage() in the way you intend reliably.
Long answer: The reason is that startTrackPage() method is only defined in the JS that is downloaded from CDN, so until it is downloaded it is not defined.
What you could do it something like:
appInsights.queue.push(function(){appInsights.startTrackPage();})
however this would not produce correct measurement, because tracking won't start right away.
So your best approach would be record start time manually, however even that you cannot do reliably. First of all you absolutely don't want to use onunload event as at this point it will be too late for Application Insights SDK to send the data so it will most likely get lost. Using onbeforeunload and flush() will help with this problem a little bit:
var pageStart = +new Date;
window.addEventListener("beforeunload", function () {
appInsights.trackMetric("timeOnPage", (+new Date)-pageStart);
appInsights.flush();
});
However even when using onbeforeunload you are looking at high number of potential data losses - you cannot guarantee that ajax request to send data to Application Insights will complete before page navigates away and connection is interrupted. In my testing with IE was getting about 50% of losses.

Detecting browsers that don't support onunload/onbeforeunload

Of all the browsers, it seems that only Opera doesn't support onunload/onbeforeunload events. (It's been fifteen years now, Opera!) Solutions for this issue have been covered many times, here for example: onbeforeunload support detection
Unfortunately, as of Opera 11.51, ("onbeforeunload" in window) == true, but the actual onbeforeunload event is never executed!
My web application needs to send data to server when a user leaves the page; I'm using a synchronous ajax request for this. It looks like I have to resort to using a "Save" button somewhere on the page to cover up for Opera issues. However, I don't want this button to confuse users whose browsers are capable of auto-saving through ajax, so I'd really like the button to only show up in Opera.
Is my only choice browser-detection? The problem is, Opera has an option to disguise itself as other browsers anyway.
I can't reproduce your finding that 'onbeforeunload' in window is true in Opera 11.5x. This is the best way to do it and should still work. Are you sure you haven't left in some definition somewhere, e.g. you've written
onbeforeunload = function (){ ... }
later in the same script that does the feature detection? If you do alert(window.onbeforeunload), what do you see? Could you share a link to the page with the problem?
Opera screwed the pooch on this one. I detect for Opera by looking for window.opera and if it exists, I deny Opera what it can't handle.
Using unload is no good I think, because it occurs too late in the game. Sometimes onbeforeunload is the only thing that'll do the trick. Once again, I just look for opera on the window object, and, if it exists, deny it the things it can't do. :)
PPK talks about it here: http://www.quirksmode.org/js/detect.html
For anyone stumbling across this post, this is a code snippet I use for detecting onbeforeunload support and if the browser doesn't support it I switch to onunload (note the use of jquery, obviously not required). In my case I use this code (and a little extra) to check if any AJAX requests are still active and stop the user navigating away. Keep in mind that using onunload isn't ideal because the browser will still navigate away from the page but at least it gives you a chance to warn the user that data might have been lost and they should go back and check.
You'll notice I'm using the isEventSupported() function available at https://github.com/kangax/iseventsupported for cross browser support detecting available events.
// If the browser supports 'onbeforeunload'
if (isEventSupported('beforeunload', window)) {
$(window).on('beforeunload', function(){
return 'This page is still sending or receiving data from our server, if you recently submitted data on this page please wait a little longer before leaving.';
});
}
// The browser doesn't support 'onbeforeunload' (Such as Opera), do the next best thing 'onunload'.
else {
$(window).on('unload', function(){
alert('This page was still sending or receiving data from our server when you navigated away from it, we would recommend navigating back to the page and ensure your data was submitted.');
});
}
See my answer to a similar / duplicated question. Basically, it sets up detection on the very first page on your domain and stores that detection result for all subsequent pages in localStorage. Including working example code.

how can I force IE9 to "see" the most current javascript when using the debugger?

I'm using IE9 to debug a web app. I made some changes to the javascript after loading the page. I'm not able to get IE9 to stop on the new code. The message is "The code in the document is not loaded". I can set breakpoints when I'm not debugging, but they won't be valid when I start debugging. I'm using IE7 Browswer Mode, IE7 Document Mode.
Things I've tried:
close dev tools window, re-open
stop debugging, start debugging
Ctrl R in dev tools window (same as Clear Browser Cache button)
Ctrl R on the IE9 web page
Ctrl F5 on the Ie9 web page
Clear browser cache for this domain
Check (set) Always refresh cache from server
Next thing to try (I guess) would be closing IE completely. Is that the fix for this? If so, yuck. It takes me a couple of minutes to set the page up so doing that after every JS change really stinks. I can use FF4 to develop the JS, but the JS issue I'm seeing is specific to IE7 so I have to do it this way.
>> How can I get IE9 (running in IE7 mode) to reliably debug the most current JS from the server?
This issue wasn't related to caching etc. IE9 was hitting a script error (missing closing paren) in the new code and not allowing breakpoints anywhere in the script. IE seemed very quiet about the script error though. Anyway, fixing the script error fixed the issues with breakpoints / caching.
If you have access to the code:
In you javascript file reference add a query string, something like this:
<script src="Scripts/main.js?v=1" type="text/javascript"></script>
And every time you change in the js file change the v value to something else, like that the browser will feel that this is a new file and will get it.
Add this:
window.applicationCache.addEventListener('updateready', function (e)
{
if (window.applicationCache.status == window.applicationCache.UPDATEREADY)
{
window.applicationCache.swapCache();
if (confirm('A new version of this site is available. Load it?'))
window.location.reload();
}
}, false);
I found this solution somwhere in the Net. Sorry, but I don't remember the author. It works for me when I debug Web App with JavaScript in Visual Studio and use IE.
I found this question based on the "the code in the document is not loaded" error message. I'm not using IE7 document mode or any of that, just IE9.
Like jcollum, my issue wasn't related to caching.
I'm using MVC.Net, and someone had set up a piece of javascript to rely on a string in the ViewBag. I changed a couple things, and that ViewBag string disappeared, so the resulting javascript looked something like this:
if(!()) {
// Some code
}
Javascript died right here, and wouldn't process the rest of the code in the block. This was confusing, as it was still trying to execute javascript in a different set of script tags, but which relied on a variable set in the other block it wouldn't load.
So, basically, a syntax error was introduced via strange means, and the debugger refused to load some of the code which came after it. Another lesson on the dangers of ViewBag.

What might cause an XMLHttpRequest to never change state in Firefox?

I'm working on some old AJAX code, written in the dark dark days before jQuery. Strangely, it has been working fine for years, until today when it suddenly stopped firing its callback. Here's the basic code:
var xml = new XMLHttpRequest(); // only needs to support Firefox
xml.open("GET", myRequestURL, true);
xml.onreadystatechange = function() { alert ('test'); };
xml.send(null);
Checking the Firebug console, the request is being sent with no worries, and it is receiving the correct XML from the request URL, but the onreadystatechange function is not working at all. There's no javascript errors or anything else strange happening in the system.
I wish I could just rewrite everything using jQuery, but I don't have the time right now. What could possibly be causing this problem??
A further update - I've been able to test my code in a different browser (FFx 3.0) and it was working there, so it must be a problem with my browser. I'm running Firefox 3.5b4 on Vista, and I've tried it now with all my addons disabled with no luck. It's still really bugging me because I was working on this site yesterday (with the same browser setup) and there were no problems at all...
Actually I just took a look back in my Addons window and saw that Firebug was still enabled. If I disable Firebug, it works perfectly. If I enable it, it's broken. Firebug version 1.40.a31
is the url malformed?
have you tried putting the whole thing in a try-catch and alerting the errors (if any)
is it failing on an authorization check?
does the url in question require http-auth? (though there should be state changes in this case, I'll admit)
edit:
I have a really funny thought here. Are you using firefox 3.5 beta4? I develop a firefox extension for a browser-based game and recently discovered some odd behvaviour. With my extension or firebug observing the ajax requests made from the page, the script ccreating them would never get calledback. The request would be correctly observed and processed by both firebug and my extension (I could observe what was sent and received)... but the page itself would never hear from the request again -- like it had disappeared into a black hole.
Try turning off firebug (or at least turn off listening to 'Net' for that domain) and test it again
Known Firefox bug affecting Firebug; see http://code.google.com/p/fbug/issues/detail?id=1569&q=xhr&colspec=ID%20Type%20Status%20Owner%20Test%20Summary for details :-)
It seems unlikely that onreadystatechange would stop working. Is it possible that the 'alert' function has somehow been disabled or overridden? Can you replace the alert with some code to make a visible change in the page, and check its functionality that way? (I know, its a stretch, but it just seems so strange that onreadystatechange wouldn't work!)

Categories

Resources