How do I get around window.opener cross-domain security - javascript

I just found out that window.opener is not available in a window opened via window.open if the new URL is cross-domain, in IE. How do I detect window opener in IE
This will happen if the window starts in my domain, leaves it, and then comes back to my domain. I am attempting to have a social signup ( facebook, google, etc ) in the popup. When it completes it should close the new window and redirect the opener.
I know that Soundcloud is pulling this off, but I have no idea how. I see the URL change from theirs to Facebook, and then close.
After redirecting back to my site from 3rd party I run this:
var data = {
type : 'complete',
destination : '<?= $destination; ?>'
};
if ( window.opener ) {
window.opener.postMessage( JSON.stringify( data ), '*' );
window.close();
}
else {
alert( "Unable to find window" );
}
It alerts out in IE, even though the window was originally my domain, which then redirected to FB, then redirected back to me. I thought may since I open my site and redirect immediately from PHP that may be an issue. However even when I opened my site, did window.location.href = 'facebookssite.com' it still complained when returning.
NOTE
Social signups do not work for google, FB, etc within an iframe. I believe they disallow them for security reasons.

Do it the other way around. Track the state of the child popup window from the main (opener) window, and you could easily know when the child window has been navigated back to you domain, so you could "talk" to it again. But don't close the child window by itself. Let the opener window obtain the result from the child window and then close it.
For example, main.html:
<!DOCTYPE html>
<head>
<title>main</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<script>
window.addEventListener("message", function(ev) {
if (ev.data.message === "deliverResult") {
alert("result: " + ev.data.result);
ev.source.close();
}
});
function Go() {
var child = window.open("child.html", "_blank", "height=200,width=200");
var leftDomain = false;
var interval = setInterval(function() {
try {
if (child.document.domain === document.domain) {
if (leftDomain && child.document.readyState === "complete") {
// we're here when the child window returned to our domain
clearInterval(interval);
alert("returned: " + child.document.URL);
child.postMessage({ message: "requestResult" }, "*");
}
}
else {
// this code should never be reached,
// as the x-site security check throws
// but just in case
leftDomain = true;
}
}
catch(e) {
// we're here when the child window has been navigated away or closed
if (child.closed) {
clearInterval(interval);
alert("closed");
return;
}
// navigated to another domain
leftDomain = true;
}
}, 500);
}
</script>
</head>
<body>
<button onclick="Go()">Go</button>
</body>
child.html:
<!DOCTYPE html>
<head>
<title>child</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<script>
window.addEventListener("message", function(ev) {
if (ev.data.message === "requestResult") {
// ev.source is the opener
ev.source.postMessage({ message: "deliverResult", result: true }, "*");
}
});
</script>
</head>
<body>
Go to example.com
Then click the browser Back button when ready.
</body>
Tested with IE10.

Due to security reason, window.opener is removed when redirecting to a different domain. The browser does not bother to restore the window.opener when you're back. In your case, you could try:
1) Do your authentication inside an iframe if possible instead of using redirect.
2) In your case, I see that you need to post the data back to the parent window. You could try this instead:
In your opened window, just store your data and close normally.
var data = {
type : 'complete',
destination : '<?= $destination; ?>'
};
window.hasData = true;
window.data = data;
window.close();
Your parent window has access to your opened window and can handle its close event:
openedWindow.beforeunload = function (){
//here you could access this.data or openedWindow.data because you're on the same domain
if (this.hasData){
}
//Reason we have this check is because the beforeunload event fires whenever the user leaves your page for any reason including close, submit, clicking a link, ...
}
3) A workaround: Use a timer in your parent page to check for the closed property of the openedWindow
setInterval(function(){
if (openedWindow.closed){
}
},1000);
4) Another solution using localStorage as you're on the same domain. You parent page can listen to the event
window.addEventListener("storage", function(event){
}, true);
Your openedWindow code:
var data = {
type : 'complete',
destination : '<?= $destination; ?>'
};
if (localStorage){
localStorage.setItem(JSON.stringify(data));
}
window.close();

From your iframe, webpage, on yoursite.com ... open a new window on yoursite.com
The window redirects itself to Google, Twitter, whatever
Once done, the OAuth redirect returns the window to a page on yoursite.com
The new window, because it has the same origin as the page that opened it, can communicate via window.open

Use localStorage or IndexedDB to communicate between windows that are showing documents from the same domain but which don't have a reference to each other.
Simply have a high-speed timer checking for the data, saving another piece of data to acknowledge receipt and the other window can find it and close.
In short - you use localStorage to pass commands and can even have a library to do this and delete the commands once they are executed, and post the return values.

You can use use window.postMessage(), which is provided for this exact scenario.
Explanation: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage

In my company we've different domains and there's the case where the intranet's site must get the our public website (to finally get rid of the maintenance of duplicated data).
Inspired in Ben Vinegar i've come to this solution simple solution avoiding the :
Call to domain webpage (in my case with the same name as the external one)
local 'getInfo.php'
<?php
$idSp = (isset($_GET['idSp'])?$_GET['idSp']:null);
echo file_get_contents('http://192.168.1.10/folder/getInfo.php?idSp='.$idSp);
?>
External 'getInfo.php' return
<?php
echo '<script>window.opener.manageDisplay('.$getRes.','.$isOK.');</script>';
if($auto_close){ echo "<script>window.close();</script>"; }
?>

Related

Allowing two sites to communicate to know the current URL of an iframe

I'm trying to figure out a solution to allow an website to know what URL the user is on through an iframe.
Website 1: http://website.website.com (Remote Website, can only add javascript & html to the webpage)
Website 2: https://example.com (Fully Editable, php, html, js.. etc)
Current Code: (Of Website 2 (Example.com)
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb#">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Website.com</title>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
</head>
<body class="body_blank">
<script type="text/javascript">
jq = jQuery.noConflict();
jq(document).ready(function() {
var currentFramePath = '';
var iframe = '<iframe src="{src}" id="#iFrameContainer" style="position:fixed; top:0px; bottom:0px; right:0px; width: 100%; border: none; margin:0; padding:0; overflow: hidden; z-index:999999; height: 100%;">';
var urlFrame = getUrlParameter('currentFrame');
if(urlFrame != null && urlFrame != ''){
console.log("Frame not found");
jq('#iFrameContainer').html(iframe.replace('{src}', urlFrame));
currentFramePath = urlFrame;
}
jq('#iFrameContainer').click(function(){
console.log("Clicked in frame");
currentFramePath = jq(this).attr('href');
console.log(currentFramePath);
});
setInterval(function(){
window.location = window.location.href.split('?')[0] + '?currentFrame=' + currentFramePath;
console.log("Update Query");
}, 5000);
});
function getUrlParameter(sParam) {
var sPageURL = decodeURIComponent(window.location.search.substring(1)),
sURLVariables = sPageURL.split('&'),
sParameterName,
i;
console.log("Get Query");
for (i = 0; i < sURLVariables.length; i++) {
sParameterName = sURLVariables[i].split('=');
if (sParameterName[0] === sParam) {
return sParameterName[1] === undefined ? true : sParameterName[1];
}
}
};
</script>
<div id="wrapper" class="wrapper_blank">
<iframe src="http://website.website.com" id="#iFrameContainer" style="position:fixed; top:0px; bottom:0px; right:0px; width: 100%; border: none; margin:0; padding:0; overflow: hidden; z-index:999999; height: 100%;">
</div>
</body>
</html>
Problem
If I refresh the page (iframe) on example.com it refreshes and forgets the page that the user is/was on...
As you can see I have attempted to get it working by detecting their page through an iFrame however this is impossible due to it being on a different domain.
Solution?
I'm looking for some sort of solution to do something like described below, bare in mind there could be a better solution.
I want the website website.website.com to get the current path / url of the page the user is on (which is being viewed through an iframe) and for it to send this path/url through to example.com then example.com would update the session / temporary cookie / temporary local storage / variable... etc which would then mean it would adjust the query string to point itself to the correct URL for when the user refreshes their page resulting in the refresh correctly remembering the page they were on.
Attempt
I tried to use the postMessage function by putting the follow code on their respective sites:
Website 1 Extra Code
<script type="text/javascript">
setInterval(function() {
parent.postMessage(window.location.pathname, "https://website.com");
},1000);
</script>
Website 2 Extra Code:
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message";
eventer(messageEvent, function(e) {
console.log('Parent Message: ', e.data);
}, false);
However nothing happens, no console messages or errors... just nothing.
I've even tried copying the likes of https://blog.teamtreehouse.com/cross-domain-messaging-with-postmessage but nothing in that helped :(
Any ideas what I am doing wrong and a way to resolve it to achieve this?
Thanks
Edits
I've tried the following js inside http://website.website.com but it didn't work:
localStorage.setItem('CurrentURLChecker', window.location.href)
if (localStorage.getItem('CurrentURLChecker')) {
if (window.parent.location.href == "https://website.com/" ) {
console.log("URL FOUND");
}
}
Uncaught DOMException: Blocked a frame with origin "http://website.website.com" from accessing a cross-origin frame at http://website.website.com/:251:44
EDIT - An example
Website 1 = "http://stackoverflow.serviceprovider.com"
Website 2 = "https://stackoverflow.com"
Website 2 contains an iframe which shows the exactly what Website 1 shows.
I am never going to visit Website 1, all clicks are done on Website 2
If I was to click on a link inside the iframe and it was to navigate to: http://stackoverflow.serviceprovider.com/this-new-page/ then Website 1 should be able to detect this and store the iframes location and remember it.
Now if I refresh my browser instead of the iframe loading http://stackoverflow.serviceprovider.com it would instead load the page they actually refreshed which is http://stackoverflow.serviceprovider.com/this-new-page/
The tab/window URL will always stay on https://stackoverflow.com/ but it would be a necessity to append a query string so the links can be made sharable.
It's that simple.
For security reasons, you can only get the url for as long as the contents of the iframe, and the referencing javascript, are served from the same domain.
If the two domains are mismatched, you'll run into cross site reference scripting security restrictions.
Since you can add javascript to the website 1 (http://website.website.com) you could create a session with javascript and save the current page the user visits in the cookies (as described here). When the user visits the home page of website on (which is happening, when the user reloads the website 2) you could get this value with javascript and load the saved page (window.location.href = 'http://website.website.com/YourSavedPage').
If you don't want that redirection every time the user visits the home page of website 1, you could think about creating a own page to redirect the user to the last opened page and to open that page once, when the iframe is loaded.
It seems like the targetOrigin (second argument of postMessage) may simply not match. Do not forget that the protocol, host & port must all be an exact match.
From the markup you posted, the iframe src domain is http://website.website.com while the parent domain is https://example.com.
If you wish for http://website.website.com to communicate it's URL to https://example.com then posting a message from the iframe should read:
window.parent.postMessage(window.location.pathname, 'https://example.com');
To make sure that the targetOrigin filter is not what's causing communication issues you can also use * for testing.
It seems that you are doing the opposite in your example (passing source domain instead of target domain) and it's also very misleading that you use "website 1" to reference the embedded site and "website 2" to reference the parent site in your explanation: I would expect the opposite.
The code samples with http://website.website.com and https://example.com doesn't work because there are on different URI schemes. One is http and another is https.
So, they have to be on the same HTTP protocol for this to work(either both http or both https).
In my example, I am using parent window URL as https://parent.example.com and iframe URL as https://child.somesite.com.
In iframe Site Code:
When the iframe site loads, we are going to send a postMessage() to the parent site about the current URL by assigning event listeners using addEventListenerto anchor tags, whenever they are clicked.
So, when an anchor tag is clicked, we prevent the default flow of route, send a message about current URL to the parent window and set current window href to the anchor's href.
Code:
var a_tags = document.getElementsByTagName('a');
for(var i=0;i<a_tags.length;++i){
a_tags[i].addEventListener('click',function(event){
event.preventDefault();
var current_href = this.getAttribute('href');
var new_location = current_href.match(/^http(s)?:\/\/.+$/) !== null ? current_href : window.location.origin + current_href;// be careful about leading '/' when dealing with relative URLs.
window.parent.postMessage(new_location,'https://parent.example.com');
window.location.href = new_location;
});
}
In parent window code:
Here, we will just attach an event listener to message event and check if the event was fired from our child site itself using the referrer present in event.origin.
If it's not, we return. If it is, we update our localStorage and set the URL received to the iframe_url key.
While refreshing the page, we first check if localStorage has this key set or not. If not, we load iframe as is, else, we load the URL we have in our storage by setting it's src attribute.
Note that we make an iframe element from javascript to avoid attaching separate event handlers to deal with it's src when requested on a new tab in the window.
Code:
const IFRAME_SITE_DOMAIN = 'https://child.somesite.com';
window.addEventListener('message',function(event){
if(event.origin !== IFRAME_SITE_DOMAIN) return;
localStorage.setItem('iframe_url',event.data);
});
var iframe = document.createElement('iframe');
if(localStorage.getItem('iframe_url') === null){
iframe.setAttribute('src',IFRAME_SITE_DOMAIN);
}else{
iframe.setAttribute('src',localStorage.getItem('iframe_url'));
}
iframe.setAttribute('height','500');
iframe.setAttribute('width','500');
document.body.append(iframe);
Sharable Links:
We make a button and span for sharable user actions like so.
Code:
<button id='share_resource_state'>Share Link</button>
<span id='share_url'></span>
Now, we add the iframe's current URL in URL fragments(characters after #). Since we are adding this in a fragment, we need not worry about it's effect on server side of parent site as it is never sent to the server and plays a role purely on the client's browser.
We convert the iframe's URL to base64 using btoa() while sharing and decode it using atob() when requested on a new tab or window.
This changes the current code on parent site(main window) a bit like so.
Code:
const IFRAME_SITE_DOMAIN = 'https://child.somesite.com';
window.addEventListener('message',function(event){
if(event.origin !== IFRAME_SITE_DOMAIN) return;
localStorage.setItem('iframe_url',event.data);
});
var iframe = document.createElement('iframe');
if(localStorage.getItem('iframe_url') === null){
if(window.location.hash != ''){
try{
var decoded_string = atob(window.location.hash.substring(1));// to remove the # from the fragment and get the base64 encoded data.
if(decoded_string.indexOf('iframe_url=') !== -1){
iframe.setAttribute('src',decoded_string.split('=')[1]);// we split the string based on '=' and assign the iframe URL which was set at the time of sharing
}else{
iframe.setAttribute('src',IFRAME_SITE_DOMAIN); // we don't deal with the fragment at all since it isn't encoded for our iframe purpose.
}
}catch(e){
iframe.setAttribute('src',IFRAME_SITE_DOMAIN); // we don't deal with the fragment at all.
}
}else{
iframe.setAttribute('src',IFRAME_SITE_DOMAIN); // we set URL as is.
}
}else{
iframe.setAttribute('src',localStorage.getItem('iframe_url'));
}
iframe.setAttribute('height','500');
iframe.setAttribute('width','500');
document.body.append(iframe);
document.getElementById('share_resource_state').addEventListener('click',function(){
var iframe_sharable_url = localStorage.getItem('iframe_url') === null ? IFRAME_SITE_DOMAIN : localStorage.getItem('iframe_url');
document.getElementById('share_url').innerHTML = window.location.href.split('#')[0] + '#' + btoa('iframe_url=' + iframe_sharable_url);
});
Some pointers before we start, whenever you have a problem it is always good to check the following basics first.
Basic problem solving
Make a bare minimum proof of concept that only shows the problem and nothing else. Remove all extra markup, styling and code.
Make sure your libraries are up to date (you are using jquery 1.11.3 instead of 3.3.1).
Follow standards, conventions, best practices if you are swimming upstream you only make it harder on yourself.
Best practices used in this answer
You are advised to follow these, they are called best practices because they make life easier not harder.
script tags go at the bottom of the page
encapsulate all your own scripts with a self executing function block in order not to pollute the global namespace
using the popular and well known $ as the jQuery reference so that everyone understands each other
using use strict javascript directive will warn about problem areas in advance
terminology
parent - refers to the main document in the browser window with the iframe markup
child - refers to the document inside the parent's iframe
Cross frame access - the answer
Access child document from the parent document
To access the child document from the parent iframe we use iframe.contentWindow. Once we have the iframe window we gain access to the child document with iframe.contentWindow.document
Access parent document from the child document
To access the parent iframe from the child document we use window.frameElement. Once we have the parent iframe element we can access the parent document with window.frameElement.ownerDocument.
The basic example
Unfortunately your examples are so convoluted with numerous problems outside the scope of this question that I was compelled to re-create these pages in order to facilitate as examples.
These examples show retrieving both the child and parent location from either the child or the parent and visa versa.
The Parent - test.html
Notice the span ids parentOut and childOut which gets populated with jQuery.
<!DOCTYPE html>
<html>
<head>
<title>Website.com</title>
</head>
<body>
<h1>Parent page</h1>
<span>Parent location: <span id="parentOut"></span></span><br>
<span>Child location: <span id="childOut"></span></span><br>
<div id="wrapper">
<iframe src="test_child.html" id="#iFrameContainer" width="100%" height="300"></iframe>
</div>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
// script encapsulation
(function ($) { "use strict";
// jQuery ready
$(function() {
$('#parentOut').text(document.location);
$('#childOut').text($('iframe')[0].contentWindow.document.location);
// the iframe by tag name
console.log($('iframe')[0]);
// the iframe by id
console.log($('#iFrameContainer')[0]);
// the iframe window
console.log($('iframe')[0].contentWindow);
// the child document
console.log($('iframe')[0].contentWindow.document);
});
})(jQuery);
</script>
</body>
</html>
The Child - test_child.html
Notice the span ids parentOut and childOut which gets populated with jQuery. There are also several hyperlinks of pages that WON'T work, see topic Security policies.
<!DOCTYPE html>
<html>
<head>
<title>Website.com</title>
</head>
<body>
<h1>Child page</h1>
<span>Child location: <span id="childOut"></span></span><br>
<span>Parent location: <span id="parentOut"></span></span><br>
<h3>Some child pages that DON'T work</h3>
SecurityError: Protocols, domains, and ports must match.<br>
SecurityError: Protocols must match.<br>
X-Frame-Options SAMEORIGIN<br>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
// script encapsulation
(function ($) { "use strict";
// jQuery ready
$(function() {
$('#childOut').text(document.location);
$('#parentOut').text(window.frameElement.ownerDocument.location);
// parent iframe
console.log(window.frameElement);
// parent document
console.log(window.frameElement.ownerDocument);
});
})(jQuery);
</script>
</body>
</html>
Being notified of child location changes
To be notified of location changes on the child document we can use the events onload or onloadstart to notify the parent.
$(document).on('load' function (event) {
$(window.frameElement.ownerDocument).append($('<p>').text('The location changed to:'+this.location);
});
Security policies
As we can see this functionality is quite powerful as it completely exposes both the parent and child documents to each other and visa versa. Because this allows you access to manipulate the content there are security policies in place to prevent us from manipulating the integrity of content that we do not own.
Protocols, domains, and ports must match
There is slightly different wording for similar errors but they all boil down to the child page must have the same domain name, same port and use the same protocol as the parent or access is blocked. The first two examples on the child page will return these errors respectively.
SecurityError: Blocked a frame with origin "http://127.0.0.1:1221" from accessing a frame with origin "http://my.umt.edu". Protocols, domains, and ports must match.
SecurityError: Blocked a frame with origin "http://127.0.0.1:1221" from accessing a frame with origin "https://en.wikipedia.org". The frame requesting access has a protocol of "http", the frame being accessed has a protocol of "https". Protocols must match.
These pages are allowed to be viewed in an iframe but if and only if the children are located at http://127.0.0.1:1221 (in my case) will this functionality be allowed.
Even further security
We can also completely prevent our sites from being viewed in an iframe. By means of the X-Frame-Options http response header, if configured with SAMEORIGIN the browser will refuse the page from being loaded in the frame. See last example on child page.
Conclusion
It is much simpler to find out exactly what the problem is if we set our project aside and start again with only the problem pieces. This also makes it much easier for someone to assist and provide a useful answer.
From what I understand of your use case, what you want to do is not allowed. You can freely make use of frames on your own site with your own pages but it is not allowed to manipulate someone else's content.
nJoy!
Just a though, based on the assumption that you can access and edit to the second website yet the server does not support PHP or any other programming/scripting language and you're stuck with HTML and Javascript:
In the parent PHP page which you are embedding the iframe into, you could call the iframe with an added parameter as shown bellow:
<iframe src="http://website.website.com/example.html?parent=<?=$_SERVER['HTTP_REFERER'];?>"></iframe>
Then in the child html page you can catch the parameter passed with the GET method with JavaScript or jQuery and use it for your purpose of determining the page, as bellow:
<script>
$(document).ready(function(){
var urlParams = new URLSearchParams(window.location.search);
var parentPage = urlParams.get('parent'); //which will store "https://example.com" in the variable. Now that you have the parent page URL you can manipulate it.
});
</script>
Even if you can't edit the html, you can inject JavaScript and HTML to the DOM of the iframe page through parent page and have it immediately run by declaring it within a jQuery function like so:
(function() {
var urlParams = new URLSearchParams(window.location.search);
var parentPage = urlParams.get('parent');
})();
I hope this made at least a little bit of sense, and can be helpful in any way. Good luck with your quest.
Cheers!
1, you need iframe show the same url even after reload
2, iframe and parent cross origin
3, you can inject js in iframe pages
4, parent page fully in control
check out https://github.com/postor/iframe-url-remember
npm i && npm run start and visit http://localhost:3000
postMessage works, I will explain in detail later, I have to catch a bus
I use node to serve static and mimic cross origin, so you can use nginx apache or php serve to host the public folder, and use lan IP and localhost mimic cross origin, you may need to modify some src
public/js/index.js is for parent page
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event) {
console.log(event)
localStorage.setItem('iframesrc', event.data)
}
var src = localStorage.getItem('iframesrc')
src && (document.getElementById('iframe').src = src)
1.listen to message event, whenever new url comes write it into localStorage
2.on page load, read url from localStorage and modify src of iframe
public/js/iframe.js for the pages inside iframe
window.parent.postMessage(location.href, '*');
1.on page load, send url to parent page
it's easy and working
you can use cookie instead of localstorage then you can use php update iframe src before sending to client browser
or php session, you may need to trigger an ajax to notify server whenever url change
You could use a tracking pixel and pass the current path of the iframe as parameter:
var pathname = window.location.pathname;
var d = new Date();
var imageUrl = 'http://www.example.com/trackingpixel.php?path='
+ pathname + '&time=' + d.getTime();
var img = document.createElement('img');
img.src = imageUrl;
document.body.appendChild(img);
And in the parent domain, create the route trackingpixel.php and save the current path in the session:
if( !isset($_SESSION['time']) || ($_GET['time'] > $_SESSION['time'])) {
$_SESSION['time'] = $_GET['time'];
$_SESSION['path'] = $_GET['path'];
}
Then when you reload the page, you can get the path from the session:
if(isset($_SESSION['path'])) {
$iframeUrl = $_SESSION['path'];
}
else {
$iframeUrl = 'http://website.website.com';
}
Note that these is a slight chance this is not going to work if the reload is executed before the tracking pixel from the previous load.
PS: Nowadays ad block extensions are quite popular and they may prevent the pixel from "firing up", I would advice to test whether the pixel works with some of the popular extensions.

How to detect closing and refreshing browser tab? [duplicate]

When we refresh the page (F5, or icon in browser), it will first
trigger ONUNLOAD event. When we close the browser (X on right top icon),It will
trigger ONUNLOAD event.
Now when ONUNLOAD event is triggered, there is no way to distinguish between refresh the page or close the browser.
If you have any solution then give me.
There is a solution.
I wanted to disconnect the user on the server when the tab or browser window was closed, but not when the page was reloaded (you may want to differentiate reload/close events for a different purpose but you may benefit from my solution). I ended up with the following process, based on HTML5's local storage and client/server AJAX communication:
on your page, add an onunload to the window to the following handler (pseudo-javascript):
function myUnload(event) {
if (window.localStorage) {
// flag the page as being unloading
window.localStorage['myUnloadEventFlag']=new Date().getTime();
}
// notify the server that we want to disconnect the user in a few seconds (I used 5 seconds)
askServerToDisconnectUserInAFewSeconds(); // synchronous AJAX call
}
on your page, add a onloadon the body to the following handler (pseudo-javascript):
function myLoad(event) {
if (window.localStorage) {
var t0 = Number(window.localStorage['myUnloadEventFlag']);
if (isNaN(t0)) t0=0;
var t1=new Date().getTime();
var duration=t1-t0;
if (duration<10*1000) {
// less than 10 seconds since the previous Unload event => it's a browser reload (so cancel the disconnection request)
askServerToCancelDisconnectionRequest(); // asynchronous AJAX call
} else {
// last unload event was for a tab/window close => do whatever you want (I do nothing here)
}
}
}
on the server, collect the disconnection requests in a list and set a timer thread which inspects the list at regular intervals (I used every 20 seconds). Once a disconnection request timeout (i.e. the 5 seconds are gone), disconnect the user from the server. If a disconnection request cancelation is received in the meantime, the corresponding disconnection request is removed from the list, so that the user will not be disconnected.
This approach is also applicable if you want to differentiate between tab/window close event and followed links or submitted form . You just need to put the two event handlers on every page which contains links and forms and on every link/form landing page.
Note that I use the unload event instead of the beforeUnload event in order to manage links to attachments properly: when a user clicks on a link to an attachment (e.g. PDF file), the beforeUnload event is dispatched, then an open/save popup is raised, and nothing more (the browser does not change the displayed page and does not dispatch the unload event). If I were using the beforeUnload event (as I did before), I would have detected a page change when there is none.
This approach is limited to the browsers which support HTML5 local storage, so you would probably use specific approaches for old browsers such as MSIE7.
Other approaches based on the event.clientY are not reliable because this value is negative when clicking on the reload or tab/window close buttons, and positive when keyboard shortcuts are used to reload (e.g. F5, Ctrl-R, ...) and window closing (e.g. Alt-F4). Relying on the event X position is also not reliable because the buttons are not placed at the same position on every browser (e.g. close button at the left).
Maybe someone is still searching for an answer...
You can use SessionStorage for that! SessionStorage is not cleared when the page is reloaded but when it is closed. So basically you could set a key/value pair when the page is loaded, but before that you check if the key/value pair exists. If it does exists it means that the page was reloaded, if not it means that the user opened the page for the first time or in a new tab.
if (sessionStorage.getItem('reloaded') != null) {
console.log('page was reloaded');
} else {
console.log('page was not reloaded');
}
sessionStorage.setItem('reloaded', 'yes');
This way you can doStuff() with the onunload event (user leaves the page), and otherStuff() if the key/value pair is set (user reloaded the page).
Unfortunately inspecting the clientY/pageY value of the event, as suggested by some of the answers here, is not a reliable way to determine if the unload event is being fired by as a consequence of the user closing the page.
The reason clientY/pageY is negative when you click the browser's close button is because the close button is positioned above the top of the document (i.e. above pixel 0), but so is the reload button meaning that clicking the reload button will also result in a negative value for clientY/pageY.
Going down the path of inspecting the x co-ordinate of the event is also problematic because the browser close button is not always on the right hand side of the window (e.g. it's on the left in OS X) and because a window can be closed by closing its tab or via the keyboard.
This is a huge hack with some limitations but it will work in most practical cases.
So if you just need something that works when users use the ctrl+r or cmd+r shortcut, you can keep track of whether r is pressed when whatever you need to do upon reload/close gets run.
Simply create keydown and keyup event listeners that toggle a rDown variable.
let rDown = false;
window.addEventListener("keydown", event => {
if (event.key == 'r')
rDown = true;
})
window.addEventListener("keyup", event => {
if (event.key == 'r')
rDown = false;
})
Then you have your "onunload" event listener where the listener function has an if statement checking if rDown is true.
window.addEventListener("onunload", () => {
if (!rDown) {
// code that only gets run when the window is closed (or
// some psychopath reloads by actually clicking the icon)
}
});
Today I had the same problem and found a possible solution that I want to share with you.
While thinking about what could help to discern between refresh and close, cookies came to my mind. I remember that setting a cookie without an explicit expiration date, renders it available only for the current session. And a current session is clearly valid until the browser is closed. This does not include closing a tab, but my problem was about user authentication and I didn't want to logout the user only for having closed a tab (I think that's the same approach as the ASPXAUTH cookie of ASP.NET).
So, I put a simple cookie in the document.cookies collection when user logged in and checked it on page load: if cookie was still there it was a refresh or a reopened tab and user data was kept, if cookie was not present session had expired so user data was cleared (same as an explicit logout).
Hope this approach can be useful to someone else!
Unfortunately there is no suggested or reliable way yet.
Use the window.onbeforeunload event for the case to navigate away from page, but it will include refreshing or anchor tags .... use a validation flag for the same, 1 example for the process is the URL check(Initial URL = current URL) or F5 check using keycode for refresh, In case of anchor tags use the bind()
Note* Keycode may cause problem in case of Chrome.
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>Test Page</title>
<style type='text/css'>
body {
font-family: sans-serif;
}
</style>
<script src="jquery-1.9.1.js" type='text/javascript'></script>
<script type='text/javascript'>
var valid=false;
function wireUpEvents() {
if(valid){
alert("Page Refreshed or Redirected");
}else{
window.onbeforeunload = askWhetherToClose;
}
function askWhetherToClose(event) {
if(!valid){
var msg;
msg = "You're leaving the page, do you really want to?";
event = event || window.event;
event.returnValue = msg;
return msg;
}}
$(document).bind('keypress', function(e) {
if (e.keyCode == 116){
// or you can insert some code to check page refresh
valid = true;
//wireUpEvents();
}
});
$("a").bind("click", function() {
//To check redirection using Anchor tags
valid = true;
//wireUpEvents();
});
}
$(document).ready(function() {
wireUpEvents();
});
</script>
</head>
<body>
<p>Close the browser window, or navigate to StackOverflow</p>
</body>
</html>
Credit to https://www.anandkanatt.com/how-do-i-detect-browser-window-closed-refreshed/#comment-15892. I simplified it a little by using the opener itself to check. Tested in Chrome Version 78.0.3887.7.
You may try this:
Add a refresh-close-detector.html file. Here's the sample code:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Processing...</title>
</head>
<body>
<script>
if (this.opener) {
// the opener was refreshed, do something if you want
} else {
// the opener was closed, do something if you want
}
// you may want to close the pop up
this.close()
</script>
</body>
</html>
In the page you want to identifying between refresh and close browser actions, add an event listener to unload:
window.addEventListener('unload', () => {
open('refresh-close-detector.html', '', 'width=100,height=100');
})
I just tried this and it solved the issue:
Create a sessionStorage object which will get destroyed when the user closes the browser. We can check the sessionStorage object to find if the user has closed the browser or refreshed the page(sessionStorage object will not be destroyed on page refresh).
$(window).bind('unload', function () {
if (/Firefox[\/\s](\d+)/.test(navigator.userAgent) && new Number(RegExp.$1) >= 4) {
console.log('firefox delete');
var data = { async: false };
endSession(data);
return null;
}
else {
console.log('NON-firefox delete');
var data = { async: true };
endSession(data);
return null;
}
});
function endSession(data) {
var id = 0
if (window) { // closeed
id=1
}
$.ajax({
url: '/api/commonAPI/'+id+'?Action=ForceEndSession',
type: "get",
data: {},
async: data.async,
success: function () {
console.log('Forced End Session');
}
});
}
Use if (window) to determines if closed or just reload. working for me.
Its a working solution
export class BootstrapComponent implements OnInit {
validNavigation = 0;
constructor(
private auth: AuthenticationService
) { }
ngOnInit() {
const self = this;
self.registerDOMEvents();
}
registerDOMEvents() {
const self = this;
window.addEventListener('unload', () => {
if (self.validNavigation === 0) {
self.endSession();
}
});
document.addEventListener('keydown', (e) => {
const key = e.which || e.keyCode;
if (key === 116) {
self.validNavigation = 1;
}
});
}
endSession() {
const self = this;
self.auth.clearStorage();
}
}
My earlier solution worked for me in IE. window.event would be undefined for browsers other than IE as 'event' is globally defined in IE unlike in other browsers. You would need to supply event as a parameter in case of other browsers. Also that clientX is not defined for firefox, we should use pageX.
Try something like this....should work for IE and firefox this...
<html>
<body>
<script type="text/javascript">
window.onunload = function(e) {
// Firefox || IE
e = e || window.event;
var y = e.pageY || e.clientY;
if(y < 0) alert("Window closed");
else alert("Window refreshed");
}
</script>
</body>
</html>
<html>
<body onunload="doUnload()">
<script>
function doUnload(){
if (window.event.clientX < 0 && window.event.clientY < 0){
alert("Window closed");
}
else{
alert("Window refreshed");
}
}
</script>
</body>
</html>

Window.open only if the window is not open

I have a link on my site that opens a new window to a page that plays a very long audio file. My current script works fine to open the page and not refresh if the link is clicked multiple times. However, when I have moved to a seperate page on my site and click this link again, it reloads. I am aware that when the parent element changes, I will lose my variable and thus I will need to open the window, overiding the existing content. I am trying to find a solution around that. I would prefer not to use a cookie to achieve this, but I will if required.
My script is as follows:
function OpenWindow(){
if(typeof(winRef) == 'undefined' || winRef.closed){
//create new
winRef = window.open('http://samplesite/page','winPop','sampleListOfOptions');
} else {
//give it focus (in case it got burried)
winRef.focus();
}
}
You should first to call winRef = window.open("", "winPopup") without URL - this will return a window, if it exists, without reloading. And only if winRef is null or empty window, then create new window.
Here is my test code:
var winRef;
function OpenWindow()
{
if(typeof(winRef) == 'undefined' || winRef.closed)
{
//create new
var url = 'http://someurl';
winRef = window.open('', 'winPop', 'sampleListOfOptions');
if(winRef == null || winRef.document.location.href != url)
{
winRef = window.open(url, 'winPop');
}
}
else
{
//give it focus (in case it got burried)
winRef.focus();
}
}
It works.
Thanks to Stan and http://ektaraval.blogspot.ca/2011/05/how-to-set-focus-to-child-window.html
My solution creates a breakout pop-up mp3 player that remains active site wide and only refreshes if the window is not open prior to clicking the link button
function OpenWindow(){
var targetWin = window.open('','winPop', 'sample-options');
if(targetWin.location == 'about:blank'){
//create new
targetWin.location.href = 'http://site/megaplayer';
targetWin.focus();
} else {
//give it focus (in case it got burried)
targetWin.focus();
}
}
Like you said, after navigating away from original page you're losing track of what windows you may have opened.
As far as I can tell, there's no way to "regain" reference to that particular window. You may (using cookies, server side session or whatever) know that window was opened already, but you won't ever have a direct access to it from different page (even on the same domain). This kind of communication between already opened windows may be simulated with help of ajax and server side code, that would serve as agent when sharing some information between two windows. It's not an easy nor clean solution however.

How to reload http window.opener from https popup?

I have an http window which opens a secure popup to submit a form to a third party web site. After the popup is closed, I would like to reload the opener so that it reflects the results of the form submission.
Since the opener and the popup use different protocols (http and https), I can't do it in the straightforward way (window.opener.location.reload()). Is there another way? Do I have to venture into JSONP?
Kludgy way: set timeout to check if opened popup was closed.
function checkClosed(){
if(new_win.closed){
alert('Window was closed');
}
else{
window.setTimeout(checkClosed,1000);
}
}
new_win = window.open('about:blank');
window.setTimeout(checkClosed,1000);
I know that this is a super old question, but I found a better way to do this: Cross-document messaging:
$('a#popup').on('click', function(event) {
event.preventDefault();
$this = $(this);
authenticationWindow = window.open($this.href, 'authenticationPopup');
});
window.addEventListener('message', function (evt) {
// simplified, should check for matching event.origin
if (event.data == 'OK' && event.origin == window.location.protocol+'//'+window.location.host) {
authenticationWindow.close();
}
});
While from the pop up (of course if you control it, at least the last page) you need this:
opener.postMessage('OK', window.location.protocol+'//'+window.location.host);
This will close the popup, and allows you to do other stuff as well, like update data via Ajax or reload the page.

Identifying Between Refresh And Close Browser Actions

When we refresh the page (F5, or icon in browser), it will first
trigger ONUNLOAD event. When we close the browser (X on right top icon),It will
trigger ONUNLOAD event.
Now when ONUNLOAD event is triggered, there is no way to distinguish between refresh the page or close the browser.
If you have any solution then give me.
There is a solution.
I wanted to disconnect the user on the server when the tab or browser window was closed, but not when the page was reloaded (you may want to differentiate reload/close events for a different purpose but you may benefit from my solution). I ended up with the following process, based on HTML5's local storage and client/server AJAX communication:
on your page, add an onunload to the window to the following handler (pseudo-javascript):
function myUnload(event) {
if (window.localStorage) {
// flag the page as being unloading
window.localStorage['myUnloadEventFlag']=new Date().getTime();
}
// notify the server that we want to disconnect the user in a few seconds (I used 5 seconds)
askServerToDisconnectUserInAFewSeconds(); // synchronous AJAX call
}
on your page, add a onloadon the body to the following handler (pseudo-javascript):
function myLoad(event) {
if (window.localStorage) {
var t0 = Number(window.localStorage['myUnloadEventFlag']);
if (isNaN(t0)) t0=0;
var t1=new Date().getTime();
var duration=t1-t0;
if (duration<10*1000) {
// less than 10 seconds since the previous Unload event => it's a browser reload (so cancel the disconnection request)
askServerToCancelDisconnectionRequest(); // asynchronous AJAX call
} else {
// last unload event was for a tab/window close => do whatever you want (I do nothing here)
}
}
}
on the server, collect the disconnection requests in a list and set a timer thread which inspects the list at regular intervals (I used every 20 seconds). Once a disconnection request timeout (i.e. the 5 seconds are gone), disconnect the user from the server. If a disconnection request cancelation is received in the meantime, the corresponding disconnection request is removed from the list, so that the user will not be disconnected.
This approach is also applicable if you want to differentiate between tab/window close event and followed links or submitted form . You just need to put the two event handlers on every page which contains links and forms and on every link/form landing page.
Note that I use the unload event instead of the beforeUnload event in order to manage links to attachments properly: when a user clicks on a link to an attachment (e.g. PDF file), the beforeUnload event is dispatched, then an open/save popup is raised, and nothing more (the browser does not change the displayed page and does not dispatch the unload event). If I were using the beforeUnload event (as I did before), I would have detected a page change when there is none.
This approach is limited to the browsers which support HTML5 local storage, so you would probably use specific approaches for old browsers such as MSIE7.
Other approaches based on the event.clientY are not reliable because this value is negative when clicking on the reload or tab/window close buttons, and positive when keyboard shortcuts are used to reload (e.g. F5, Ctrl-R, ...) and window closing (e.g. Alt-F4). Relying on the event X position is also not reliable because the buttons are not placed at the same position on every browser (e.g. close button at the left).
Maybe someone is still searching for an answer...
You can use SessionStorage for that! SessionStorage is not cleared when the page is reloaded but when it is closed. So basically you could set a key/value pair when the page is loaded, but before that you check if the key/value pair exists. If it does exists it means that the page was reloaded, if not it means that the user opened the page for the first time or in a new tab.
if (sessionStorage.getItem('reloaded') != null) {
console.log('page was reloaded');
} else {
console.log('page was not reloaded');
}
sessionStorage.setItem('reloaded', 'yes');
This way you can doStuff() with the onunload event (user leaves the page), and otherStuff() if the key/value pair is set (user reloaded the page).
Unfortunately inspecting the clientY/pageY value of the event, as suggested by some of the answers here, is not a reliable way to determine if the unload event is being fired by as a consequence of the user closing the page.
The reason clientY/pageY is negative when you click the browser's close button is because the close button is positioned above the top of the document (i.e. above pixel 0), but so is the reload button meaning that clicking the reload button will also result in a negative value for clientY/pageY.
Going down the path of inspecting the x co-ordinate of the event is also problematic because the browser close button is not always on the right hand side of the window (e.g. it's on the left in OS X) and because a window can be closed by closing its tab or via the keyboard.
This is a huge hack with some limitations but it will work in most practical cases.
So if you just need something that works when users use the ctrl+r or cmd+r shortcut, you can keep track of whether r is pressed when whatever you need to do upon reload/close gets run.
Simply create keydown and keyup event listeners that toggle a rDown variable.
let rDown = false;
window.addEventListener("keydown", event => {
if (event.key == 'r')
rDown = true;
})
window.addEventListener("keyup", event => {
if (event.key == 'r')
rDown = false;
})
Then you have your "onunload" event listener where the listener function has an if statement checking if rDown is true.
window.addEventListener("onunload", () => {
if (!rDown) {
// code that only gets run when the window is closed (or
// some psychopath reloads by actually clicking the icon)
}
});
Today I had the same problem and found a possible solution that I want to share with you.
While thinking about what could help to discern between refresh and close, cookies came to my mind. I remember that setting a cookie without an explicit expiration date, renders it available only for the current session. And a current session is clearly valid until the browser is closed. This does not include closing a tab, but my problem was about user authentication and I didn't want to logout the user only for having closed a tab (I think that's the same approach as the ASPXAUTH cookie of ASP.NET).
So, I put a simple cookie in the document.cookies collection when user logged in and checked it on page load: if cookie was still there it was a refresh or a reopened tab and user data was kept, if cookie was not present session had expired so user data was cleared (same as an explicit logout).
Hope this approach can be useful to someone else!
Unfortunately there is no suggested or reliable way yet.
Use the window.onbeforeunload event for the case to navigate away from page, but it will include refreshing or anchor tags .... use a validation flag for the same, 1 example for the process is the URL check(Initial URL = current URL) or F5 check using keycode for refresh, In case of anchor tags use the bind()
Note* Keycode may cause problem in case of Chrome.
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>Test Page</title>
<style type='text/css'>
body {
font-family: sans-serif;
}
</style>
<script src="jquery-1.9.1.js" type='text/javascript'></script>
<script type='text/javascript'>
var valid=false;
function wireUpEvents() {
if(valid){
alert("Page Refreshed or Redirected");
}else{
window.onbeforeunload = askWhetherToClose;
}
function askWhetherToClose(event) {
if(!valid){
var msg;
msg = "You're leaving the page, do you really want to?";
event = event || window.event;
event.returnValue = msg;
return msg;
}}
$(document).bind('keypress', function(e) {
if (e.keyCode == 116){
// or you can insert some code to check page refresh
valid = true;
//wireUpEvents();
}
});
$("a").bind("click", function() {
//To check redirection using Anchor tags
valid = true;
//wireUpEvents();
});
}
$(document).ready(function() {
wireUpEvents();
});
</script>
</head>
<body>
<p>Close the browser window, or navigate to StackOverflow</p>
</body>
</html>
Credit to https://www.anandkanatt.com/how-do-i-detect-browser-window-closed-refreshed/#comment-15892. I simplified it a little by using the opener itself to check. Tested in Chrome Version 78.0.3887.7.
You may try this:
Add a refresh-close-detector.html file. Here's the sample code:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Processing...</title>
</head>
<body>
<script>
if (this.opener) {
// the opener was refreshed, do something if you want
} else {
// the opener was closed, do something if you want
}
// you may want to close the pop up
this.close()
</script>
</body>
</html>
In the page you want to identifying between refresh and close browser actions, add an event listener to unload:
window.addEventListener('unload', () => {
open('refresh-close-detector.html', '', 'width=100,height=100');
})
I just tried this and it solved the issue:
Create a sessionStorage object which will get destroyed when the user closes the browser. We can check the sessionStorage object to find if the user has closed the browser or refreshed the page(sessionStorage object will not be destroyed on page refresh).
$(window).bind('unload', function () {
if (/Firefox[\/\s](\d+)/.test(navigator.userAgent) && new Number(RegExp.$1) >= 4) {
console.log('firefox delete');
var data = { async: false };
endSession(data);
return null;
}
else {
console.log('NON-firefox delete');
var data = { async: true };
endSession(data);
return null;
}
});
function endSession(data) {
var id = 0
if (window) { // closeed
id=1
}
$.ajax({
url: '/api/commonAPI/'+id+'?Action=ForceEndSession',
type: "get",
data: {},
async: data.async,
success: function () {
console.log('Forced End Session');
}
});
}
Use if (window) to determines if closed or just reload. working for me.
Its a working solution
export class BootstrapComponent implements OnInit {
validNavigation = 0;
constructor(
private auth: AuthenticationService
) { }
ngOnInit() {
const self = this;
self.registerDOMEvents();
}
registerDOMEvents() {
const self = this;
window.addEventListener('unload', () => {
if (self.validNavigation === 0) {
self.endSession();
}
});
document.addEventListener('keydown', (e) => {
const key = e.which || e.keyCode;
if (key === 116) {
self.validNavigation = 1;
}
});
}
endSession() {
const self = this;
self.auth.clearStorage();
}
}
My earlier solution worked for me in IE. window.event would be undefined for browsers other than IE as 'event' is globally defined in IE unlike in other browsers. You would need to supply event as a parameter in case of other browsers. Also that clientX is not defined for firefox, we should use pageX.
Try something like this....should work for IE and firefox this...
<html>
<body>
<script type="text/javascript">
window.onunload = function(e) {
// Firefox || IE
e = e || window.event;
var y = e.pageY || e.clientY;
if(y < 0) alert("Window closed");
else alert("Window refreshed");
}
</script>
</body>
</html>
<html>
<body onunload="doUnload()">
<script>
function doUnload(){
if (window.event.clientX < 0 && window.event.clientY < 0){
alert("Window closed");
}
else{
alert("Window refreshed");
}
}
</script>
</body>
</html>

Categories