This is a follow up to my original question here. The answers proposed work in Firefox, but there are problems with at least Chrome and Safari (on iOS).
The initial issue is this: on an unrelated site (say Facebook), users can create links where the href is in the form http//www.siteA.com/?http://www.siteB.com. The intention is that siteA parses the querystring and re-directs the browser to siteB. That all works fine.
Then, when a user, having been re-directed to siteB, clicks the back button on their browser, the goal is that they should return to siteA and not be re-directed again.
The answer to my previous question proposed that at the time of the re-direction from siteA, the code on siteA checks for a cookie - if it is not there, it sets it and re-directs. If it is there, then no redirection. In order to allow the user to return to the original referring page and click the same link again (and be re-directed to siteB), it was also proposed that if the cookie is found on siteA, as well as no re-direction, the cookie is deleted.
On Firefox that all works. The 2 problems now are:
on Chrome (and maybe others), the cookie deletion either doesn't work, or works only after the user navigates to another site. The deletion code is just simple javascript, setting the same cookie with an expiry date in the past. This may in practice be a relatively minor issue, but it would be nice to find a solution.
on Safari on iOS, siteA is not in the browser history. It seems iOS (and maybe Safari generally), tries to avoid the looping problem) of returning to a page that re-directed to a second site), by omitting the re-directing page from the history stack. As a result, the pressing the back button on siteB goes to the page prior to the re-directing page. This is a major issue.
It seems there are 3 possibilities - what I want to do is not possible because it's a security risk; there's no crosss-browser/platform solution; or I've approached the goal by completely the wrong method.
The fixed points are:
the form of the URL (with a querystring containing the second URL);
no access to the server (limited to javascript/jquery).
no control over siteB (only siteA).
I'd be grateful for any suggestions and/or advice.
Thanks
This appears to be a solution to issue 2:
$(document).ready(function() {
var s = location.search;
if(s != '') {
var split = s.split('?');
var loc = split[1].replace('?', '');
if (document.cookie.indexOf('redirected=' + loc + '') == -1) {
document.cookie = 'redirected=' + loc + '';
var link = document.createElement('a');
link.href = loc;
document.body.appendChild(link);
link.click();
} else {
document.cookie = 'redirected=' + loc + '; expires=Thu, 01 Jan 1970 00:00:00 GMT';
var url = location.href.replace('' + s + '', '');
location.href = '' + url + '';
}
} else
{
//do something on the re-direction page
}
});
It's a bit old school, but instead of re-directing, you create a link on the intermediate page and click it programmatically. That works like a re-direction, but leaves the re-directing page in the history stack, even on iOS.
Thanks to this answer on SO for the hint.
Still looking for a way to remove the cookie more effectively though.
I'd be interested and grateful to read any other comments on these issues. Thanks.
Related
How can I force the web browser to do a hard refresh of the page via JavaScript?
Hard refresh means getting a fresh copy of the page AND refresh all the external resources (images, JavaScript, CSS, etc.).
⚠️ This solution won't work on all browsers. MDN page for location.reload():
Note: Firefox supports a non-standard forceGet boolean parameter for location.reload(), to tell Firefox to bypass its cache and force-reload the current document. However, in all other browsers, any parameter you specify in a location.reload() call will be ignored and have no effect of any kind.
Try:
location.reload(true);
When this method receives a true value as argument, it will cause the page to always be reloaded from the server. If it is false or not specified, the browser may reload the page from its cache.
More info:
The location object
window.location.href = window.location.href
Accepted answer above no longer does anything except just a normal reloading on mostly new version of web browsers today. I've tried on my recently updated Chrome all those, including location.reload(true), location.href = location.href, and <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />. None of them worked.
My solution is by using server-side capability to append non-repeating query string to all included source files reference as like below example.
<script src="script.js?t=<?=time();?>"></script>
So you also need to control it dynamically when to keep previous file and when to update it. The only issue is when files inclusion is performed via script by plugins you have no control to modify it. Don't worry about source files flooding. When older file is unlinked it will be automatically garbage collected.
Changing the current URL with a search parameter will cause browsers to pass that same parameter to the server, which in other words, forces a refresh.
(No guarantees if you use intercept with a Service Worker though.)
const url = new URL(window.location.href);
url.searchParams.set('reloadTime', Date.now().toString());
window.location.href = url.toString();
If you want support older browsers:
if ('URL' in window) {
const url = new URL(window.location.href);
url.searchParams.set('reloadTime', Date.now().toString());
window.location.href = url.toString();
} else {
window.location.href = window.location.origin
+ window.location.pathname
+ window.location.search
+ (window.location.search ? '&' : '?')
+ 'reloadTime='
+ Date.now().toString()
+ window.location.hash;
}
That said, forcing all your CSS and JS to refresh is a bit more laborious. You would want to do the same process of adding a searchParam for all the src attributes in <script> and href in <link>. That said it won't unload the current JS, but would work fine for CSS.
document.querySelectorAll('link').forEach((link) => link.href = addTimestamp(link.href));
I won't bother with a JS sample since it'll likely just cause problems.
You can save this hassle by adding a timestamp as a search param in your JS and CSS links when compiling the HTML.
This is a 2022 update with 2 methods, considering SPA's with # in url:
METHOD 1:
As mentioned in other answers one solution would be to put a random parameter to query string. In javascript it could be achieved with this:
function urlWithRndQueryParam(url, paramName) {
const ulrArr = url.split('#');
const urlQry = ulrArr[0].split('?');
const usp = new URLSearchParams(urlQry[1] || '');
usp.set(paramName || '_z', `${Date.now()}`);
urlQry[1] = usp.toString();
ulrArr[0] = urlQry.join('?');
return ulrArr.join('#');
}
function handleHardReload(url) {
window.location.href = urlWithRndQueryParam(url);
// This is to ensure reload with url's having '#'
window.location.reload();
}
handleHardReload(window.location.href);
The bad part is that it changes the current url and sometimes, in clean url's, it could seem little bit ugly for users.
METHOD 2:
Taking the idea from https://splunktool.com/force-a-reload-of-page-in-chrome-using-javascript-no-cache, the process could be to get the url without cache first and then reload the page:
async function handleHardReload(url) {
await fetch(url, {
headers: {
Pragma: 'no-cache',
Expires: '-1',
'Cache-Control': 'no-cache',
},
});
window.location.href = url;
// This is to ensure reload with url's having '#'
window.location.reload();
}
handleHardReload(window.location.href);
Could be even combined with method 1, but I think that with headers should be enought:
async function handleHardReload(url) {
const newUrl = urlWithRndQueryParam(url);
await fetch(newUrl, {
headers: {
Pragma: 'no-cache',
Expires: '-1',
'Cache-Control': 'no-cache',
},
});
window.location.href = url;
// This is to ensure reload with url's having '#'
window.location.reload();
}
handleHardReload(window.location.href);
UPDATED to refresh all the external resources (images, JavaScript, CSS, etc.)
Put this in file named HardRefresh.js:
function hardRefresh() {
const t = parseInt(Date.now() / 10000); //10s tics
const x = localStorage.getItem("t");
localStorage.setItem("t", t);
if (x != t) location.reload(true) //force page refresh from server
else { //refreshed from server within 10s
const a = document.querySelectorAll("a, link, script, img")
var n = a.length
while(n--) {
var tag = a[n]
var url = new URL(tag.href || tag.src);
url.searchParams.set('r', t.toString());
tag.href = url.toString(); //a, link, ...
tag.src = tag.href; //rerun script, refresh img
}
}
}
window.addEventListener("DOMContentLoaded", hardRefresh);
window.addEventListener("deviceorientation", hardRefresh, true);
This code do a fully controled forced hard refresh for every visitor, so that any update will show up without a cashing problem.
Duplicated DOM rendering is not a performance issue, because the first render is from cache and it stops rendering in <script src="js/HardRefresh.js"> where it reload a page from server. When it run a refreshed page it also refresh urls in page.
The last refresh time x is stored in localStorage. It is compared with the current time t to refresh within 10 seconds. Assuming a load from server not take more than 10 sec we manage to stop a page refresh loop, so do not have it less than 10s.
For a visitor of page the x != t is true since long time ago or first visit; that will get page from server. Then diff is less than 10s and x == t, that will make the else part add query strings to href and src having sources to refresh.
The refresh() function can be called by a button or other conditioned ways. Full control is managed by refining exclusion and inclusion of urls in your code.
For angular users and as found here, you can do the following:
<form [action]="myAppURL" method="POST" #refreshForm></form>
import { Component, OnInit, ViewChild } from '#angular/core';
#Component({
// ...
})
export class FooComponent {
#ViewChild('refreshForm', { static: false }) refreshForm;
forceReload() {
this.refreshForm.nativeElement.submit();
}
}
The reason why it worked was explained on this website: https://www.xspdf.com/resolution/52192666.html
You'll also find how the hard reload works for every framework and more in this article
explanation: Angular
Location: reload(), The Location.reload() method reloads the current URL, like the Refresh button. Using only location.reload(); is not a solution if you want to perform a force-reload (as done with e.g. Ctrl + F5) in order to reload all resources from the server and not from the browser cache. The solution to this issue is, to execute a POST request to the current location as this always makes the browser to reload everything.
The most reliable way I've found is to use a chache buster by adding a value to the querystring.
Here's a generic routine that I use:
function reloadUrl() {
// cache busting: Reliable but modifies URL
var queryParams = new URLSearchParams(window.location.search);
queryParams.set("lr", new Date().getTime());
var query = queryParams.toString();
window.location.search = query; // navigates
}
Calling this will produce something like this:
https://somesite.com/page?lr=1665958485293
after a reload.
This works to force reload every time, but the caveat is that the URL changes. In most applications this won't matter, but if the server relies on specific parameters this can cause potential side effects.
I want to redirect a user to a different domain, but only when he's using Chrome.
The new domain redirects to the same server as the old domain. Literally I just want to display a different domain, when a user is using chrome.
This is what I have came up with:
<script>parseInt(navigator.appVersion.match(/.*Chrome\/([0-9\.]+)/)[1]) >= 64 ? document.location = "https://newdomain.com" + window.location.pathname : true</script>
the problem with this script is, that when someone is using chrome and goes to my old domain it ends in a redirect loop and never stops. Can anyone help? I guess I need to specify somehow, that when the user is redirected to the new domain the script stops.
You can pass a URL parameter that will stop the loop of redirection.
"https://newdomain.com" + window.location.pathname + ?redirected=true
You can read it and check if it was already redirected before.
const queryString = window.location.search;
const redirected = new URLSearchParams(queryString).get('redirected');
if (!redirected) {
// your code for redirection
}
You need check browser type and current hostname before you decide to redirect, something like;
const isChrome = parseInt(navigator.appVersion.match(/.*Chrome\/([0-9\.]+)/)[1]) >= 64
if(isChrome && window.location.hostname != "YOUR_OLD_DOAMINNAME){
document.location = "https://newdomain.com" + window.location.pathname
}
Okay thanks guys, its now working with this code:
const isChrome = parseInt(navigator.appVersion.match(/.*Chrome\/([0-9\.]+)/)[1]) >= 64
if(isChrome && window.location.hostname == "olddomain"){
var newurl = location.href;
newurl = newurl.replace(/^.*\/\/[^\/]+/, '')
document.location = "newdomain" + newurl
}
The only problem I seem to face now is, that it also directs users who use microsoft edge because it uses the chrome user agent. But thats not really a big problem.
I used location.href because when using location.pathname its not the full url.
So what this code does now, it first checks the user agent and if the user is using the Chrome browser. It then redirects the user to another domain that points to the same host as the old domain. The only thing that changes for the user is the domain.
I'm working on a phaser game that's to be embedded in a website via iframe. The game supports multiple languages, so we've taken to using the site the game was accessed from as an indicator (phaser-game.com/ru would be in Russian, phaser-game.com/ar would be in Arabic, etc).
Here's the code so far (fired via window.addEventListener('load', getDomainSetLanguage);:
function getDomainSetLanguage()
{
let url = (window.location !== window.parent.location) ? document.referrer : document.location.href;
console.log('url = ' + url);
for (let i = 0; i < COUNTRIES_DOMAIN.length; i++)
{
if (url.indexOf(COUNTRIES_DOMAIN[i].URL) >= 0)
{
DOMAIN_ID = COUNTRIES_DOMAIN[i].ID;
LANGUAGE_ID = COUNTRIES_DOMAIN[i].LANGUAGE_ID;
break;
}
}
if (DOMAIN_ID === -1)
{
DOMAIN_ID = 1;
}
if (LANGUAGE_ID === -1)
{
LANGUAGE_ID = 1;
}
console.log('DOMAIN_ID = ' + DOMAIN_ID + "; LANGUAGE_ID = " + LANGUAGE_ID);
}
Now this works fine, on the surface. However, the game does trigger a reload every now and then, and when the game comes back, it now gets it's own URL, not the parent's / iframe's.
This has the result of the game language defaulting to English.
Note that this only occurs in Chrome and Safari. FireFox works just fine.
Is there something I'm missing? Or is there anything else I can try?
I've tried logging the values of document.referrer and document.location.href, but I'm just getting browser errors about permissions and stuff and the game defaults to English.
I read from here that Chrome (and possibly Safari) doesn't fire the onload function of objects in the iframe, but I'm not sure if this applies to me, as I have a lot of other functions tied to onload that do work.
It should be mentioned that I cannot modify the iframe itself, so any solution must be from the game itself.
Thanks!
let url = (window.location !== window.parent.location) ? document.referrer : document.location.href;
This line from your code makes it so that when you're inside of an iframe, document.referrer is used as the URL to determine the language from.
As per the MDN page on Document.referrer:
The Document.referrer property returns the URI of the page that linked to this page.
Inside an <iframe>, the Document.referrer will initially be set to the same value as the href of the parent window's Window.location.
This means it will work on initial load just fine, as you've experienced.
As far as I can tell, the specification isn't explicit about how to handle reloading. This is probably the cause of the differences in browser behaviour. It isn't too crazy to think that is should be empty after a reload, as it wasn't loaded from the parent page that time around.
An alternate solution would be to use window.parent.location.href, which always refers to the URL of the iframe's parent window (read more in Difference between document.referrer and window.parent.location.href).
Your line of code could look something like this:
// if parent and child href are equal, using either yields the same result
// if there is no parent, window.parent will be equal to window
// therefore, the conditional statement isn't necessary
let url = window.parent.location.href;
This has probably been answered before but i don't understand al the difficult research online so i ask it here And hope for a easy answer
<script type="text/javascript"> // <![CDATA[
if ((navigator.userAgent.indexOf('iPad') != -1)) {
location.replace = "http://www.joey-games.byethost4.com/games/";
} // ]]>
This does not redirect me.
var isiPad = navigator.userAgent.match(/iPad/i) != null;
//or by using
var ua = navigator.userAgent;
var isiPad = /iPad/i.test(ua)
You can find other related information in the following links:
http://fellowtuts.com/jquery/ipadiphone-detection-using-javascript/
Detect iPad users using jQuery?
Instead of using location.replace use location.href
Your snippet becomes
if (navigator.userAgent.indexOf('iPad') > -1) {
location.href = 'http://www.joey-games.byethost4.com/games/';
}
I've made two changes to your code
!= to > in the if statement which is no biggie (nothing relevant)
changed method call from replace to href Taken from MDN
The Location.replace() method replaces the current resource with the one at the provided URL. The difference from the assign() method is that after using replace() the current page will not be saved in session History, meaning the user won't be able to use the back button to navigate to it.
This basically says that if you use replace you cannot use the browsers back button to get to this page anymore, basically reducing the users user-experience since they'll get confused about what the back button does when they use your website.
This will redirect your users on the iPad to a different website however, you shouldn't do this - it's bad for your users and your website. (as partially explained above)
Iam Not gonna use The redirecting to go to "Joey-games.byethost4.com/games/" I wil redirect iPad users to: Joey-games.byethost4.com/mobile/iPad/ for a mobile site since flash player is not supported in safari yet
It sounds simple and I think it should be simple, but somehow I don't get it to work...
I want to set a Cookie using Javascript and this Cookie should be removed when the user quits the browser. Setting the cookie and getting the value is not the problem. But when I quit the browser and reopen it, the cookie is still there (Firefox).
I use jQuery and the Cookie-Plugin.
Here is my test code:
$(document).ready(function(){
$('#output').append( '<li>initialize...</li>' );
var $cookieVal = $.cookie('testCookie');
$('#output').append( '<li>check cookie...</li>' );
if(!$cookieVal) {
$('#output').append( '<li>set cookie...</li>' );
$.cookie('testCookie', 'eat cookies', { path: '/' });
//console.log( $.cookie() );
} else {
$('#output').append( '<li>cookie is already set...</li>' );
$('#output').append( '<li>cookie value: '+$.cookie('testCookie')+'</li>' );
}
});
Please find the working example at jsFiddle.
I am beginning to wonder if your testing method might be the problem here. So, I am going to write this in a specific way.
Actual Answer: Browser Setting
In Firefox, Options>General>When Firefox starts>"Show my windows and tabs from last time" is going to preserve your previous session. Change this setting to see that this is indeed working as it is supposed to. Firefox is prolonging your session. For further information, see this "bug": http://bugzilla.mozilla.org/show_bug.cgi?id=530594
There are similar settings in most browsers that probably behave the same way.
Original Answer:
I created a fiddle, http://jsfiddle.net/8Ahg2/ that uses document.cookie rather than jquery cookie plugin. Here is how you test this. (source below)
copy the following URL to your clipboard: http://fiddle.jshell.net/8Ahg2/show/
Completely close your browser of choice - this should be browser independent.
Open your browser, paste the url. The first time it should say: check cookie...
set cookie...
Refresh the page, notice that it should now say the value of the cookie ("test")
Close your browser completely again.
Navigate to the URL that should still be in your clipboard. *Do not refresh the page on the first view, it should again say 'check cookie...
set cookie...'
js
$(document).ready(function () {
$('#output').append('<li>initialize...</li>');
//this regex gets the "name" cookie out of the string of cookies that look like this: "name=test;var2=hello;var3=world"
var cookieVal = document.cookie.replace(/(?:(?:^|.*;\s*)name\s*\=\s*([^;]*).*$)|^.*$/, "$1");
$('#output').append('<li>check cookie...</li>');
if (!cookieVal) {
$('#output').append('<li>set cookie...</li>');
document.cookie = "name=test";
} else {
$('#output').append('<li>cookie is already set...</li>');
$('#output').append('<li>cookie value: ' + cookieVal + '</li>');
}
});
There is some code that worked for me. It should expire when you close the browser because of the date to expire being before now:
var vEnd = new Date();
vEnd.setDate(vEnd.getDate() - 1);
var endOfCookieText = "; expires=" + vEnd.toGMTString() + "; path=/";
document.cookie = escape('testCookie') + "=" + escape("eat cookies") + endOfCookieText;
FIDDLE MODIFIED
Note that the fiddle gives a bunch of load errors on the console for me.