Requirement:
Compare the previous url with the current one and do some action.
Problem:
The document.referrer is still same after the page refresh.
Question:
Is there an elegant way of comparing urls other then storing a cookie or session?
In case someone will be interested how I resolved my problem:
// store the current url
window.onbeforeunload = function(e) {
window.localStorage.setItem("current-url", window.location.href);
};
// read
var urlReferrer = window.localStorage.getItem("current-url");
// do something
if (urlReferrer != window.location.href)
JavaScript never does anything in an 'elegant' way. It's going to be tricky generally just using javascript, especially if you are expecting this to not change after a page refresh.
While I would suggest either cookies or local storage, your other alternative is saving the "state" of the page using a back-end programming language.
Related
Some URLs in my single-page-app (SPA) contain sensitive information like an access token, user information, etc.
Examples:
/callback#access_token=HBVYTU2Rugv3gUbvgIUY
/?email=username#example.com
I see that hotjar allows suppressing DOM elements and images from tracked data. Is it possible to hide params in URL or at least disable tracking for some pages?
Since you are saying that it is your SPA, you might solve the problem by switching from GET requests (which have the parameters inside the URL) to POST requests. I do not know hotjar, but if you tell the tracking service to analyze URLs only, that would be an option worth considering.
Another option frequently used is to obfuscate your parameters in the URL, see e.g. Best way to obfuscate an e-mail address on a website? However, that is never a really safe solution for sensitive data, since the de-ciphering step is too easy, in particular if your man-in-the-middle has all requests ever send to your SPA.
Edit. I just found in the Hotjar allows RegEx. Assuming you could enter a regular expression of URL-parts to exclude.
The general syntax /foo/bar/ means that foo should be replaced by bar, in our case, we want to delete the given snippet, that why it is /foo//.
For the given case of the access token, the regular expression would be
/callback#access_token=[a-zA-Z0-9]{15}//
and respectively for the email part of the URL
/\?email=(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")#(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])//
This second RegEx partially taken from How to validate an email address using a regular expression?
It seems to me that it's reasonable to assume that tracking scripts will try to access window.location.href or similar to get the current url which they will store.
So a possible solution would be create a dynamic scope which has a different value for window.location.href (with all sensitive info filtered out)
This is how it might work:
// get the tracker script as a string, so you can eval it in a dynamic scope
let trackerScript = 'console.log("Tracked url:", window.location.href)';
// now lets lock it up
function trackerJail(){
let window = {
location: {
// put your filtered url here
href: "not so fast mr.bond"
}
}
eval(String(trackerScript))
}
trackerJail()
If the tracking snippet is wrapped in a function it might be possible to create a dynamic scope for it without running eval by overriding it's prototype instead. But I'm not sure you can count on tracker scripts being wrapped in a neat function you can modify.
Also, there are a couple more ways the script might try to access the URL, so make sure to cover all the exits
If you control the page and order of scripts, you could read the data from the url then delete it before anything else can get to it.
proofOfConcept.html
<script id="firstThingToLoad.js">
console.log(window.location.href);
const keyRegex = /key=[^&]*/;
const key = window.location.href.match(keyRegex);
console.log("I have key", key);
const href = window.location.href.replace(keyRegex, "");
history.replaceState({}, "", href);
</script>
<script id="someSnoopyCode.js">
console.log("I'm snooping: ", window.location.href);
</script>
<body>
Link to private
</body>
Of course the Link to private should not exist as is. Also, this does break refresh and most navigation in general, though there are ways to catch and save that.
I have some javascript, in which i need either cookies or localstorage to ensure variables aren't lost.
I'm a beginner and i'm not sure which is best to use, but i know both do sort of the same thing.
Basically, i need whatever the javascript is doing to be stored and even when user logs out / logs in, between any amount of days this is still being saved.
Can someone help?
<script>
$(document).ready(function() {
$("input").click(function(e) {
e.preventDefault();
var $challenge_div = $(this).parent().parent();
$challenge_div.data("finish", "false");
$challenge_div.removeClass("show").addClass("hide");
var $challenge_list = $("div[class='hide']");
$.each($challenge_list, function() {
var new_challenge = $(this);
if (new_challenge.data("finish") == false) {
new_challenge.removeClass("hide").addClass("show");
return false;
}
});
if ($("div[class='show']").length == 0) {
$("#message p").html("You finished all your challenges !");
}
});
});
</script>
I'm a beginner and i'm not sure which is best to use, but i know both do sort of the same thing.
Actually, they do very different things.
Cookies are for sending information back and forth between the server and client on every HTTP request.
Web storage is for storing information in the client (only).
For your use case, web storage would be the right thing to use, since you only want the information client-side. It also has a reasonable API (unlike cookies). You can store a string:
localStorage.setItem("key", "value");
// or
localStorage.key = "value"; // See warning below
// or
localStorage["key"] = "value"; // See warning below
(I'd recommend using the setItem method instead of directly assigning to a property on localStorage, so there's no chance of your conflicting with any of localStorage's methods!)
...and get it back later, even in the user left the page entirely and came back:
var x = localStorage.getItem("key");
// or
var x = localStorage.key; // See warning above
// or
var x = localStorage["key"]; // See warning above
console.log(x); // "value"
And remove it if you want:
localStorage.removeItem("key");
Often, it makes sense to use JSON to store your client-side information, by using some kind of settings object and storing it by converting it to JSON:
localStorage.setItem("settings", JSON.stringify(settings));
...and using JSON.parse when getting it (back):
var settings = JSON.parse(localStorage.getItem("settings"));
if (!settings) {
// It wasn't there, set defaults
settings = {/*...*/};
}
I'm not sure which is best to use, but I know both do sort of the same thing.
Not really. A key difference in Cookies and Local Storage is that cookies will be sent along with every server request you make.[1]
Requesting some data using AJAX, cookies will be included in the request.
Navigating to a new page, cookies will be included in the request.
So the disadvantage is that you are transferring additional data to the server every time you make a request. More so if you are storing a lot of data into the cookies.
Local storage is merely kept in the browser (until you clear it explicitly), but it is not sent along with every request.
The decision is simple, if you require that data available to the server with request parameters, you should use cookies, else use local storage.
One more thing, from what you are saying, it seems like you intend to store the user's progress in a game. Keep in mind that both cookie and local storage are accessible to the user. So they can tamper with that data if they want.
If it is critical to prevent user's from changing the data, you must store the data on the server instead.
Do read: JavaScript: client-side vs. server-side validation
[1]. This may not be entirely true with HTTP2 but that's a different topic.
I need to be able to retrieve the list of request URLs that are displayed in the browser console, i.e: GET http://mydomain.com/index.php?p=1&curr=GBP&cat=Food. 200. Users can click around my app and apply different filters and scrolls through pages and I need some way of tracking this so that I always know what data has already been loaded for that users session.
I had thought about using PHPs $_SERVER['REQUEST_URI'] and saving them in a session but then I don't know how I would access this session from my JQuery as its JQuery that constructs the URLs.
Has anyone any idea how I can access this data from the console? Is this possible? If not can anyone suggest a workaround?
The PHP / JQuery mess I have so far:
<?php
session_start();
//keep track of requests.
if (!isset($_SESSION['requests'])) {
$_SESSION['requests'] = array();
} else {
if (!in_array( $_SERVER['REQUEST_URI'], $_SESSION['requests'])) {
$_SESSION['requests'][] = $_SERVER['REQUEST_URI'];
}
}
$requests = json_encode($_SESSION['requests']);
print_r($_SESSION['requests']);
print_r($requests); //these both have values
?>
//further down the page is the javascript
$('.filter a').click(function(e) {
var $this = $(this);
var $optionSet = $this.parents('.option-set');
var group = $optionSet.attr('data-filter-group');
filters[ group ] = $this.attr('data-filter-value');
//***more code for filtering etc******/
var paginate_url = $('.paginate a').attr('href');
//THIS IS PART I CANNOT GET WORKING
var visited_urls= <?=$requests?>;
//console.log($.parseJSON(visited_urls));
console.log(visited_urls); //is always empty
var pageno = ''; //somehow check to see if the URL that has been clicked exists int he requests array, if so get the page number and increment.
var next_url = UpdateQueryString(paginate_url, pageno, group, encodeURIComponent(filter_qry));
I'm not completely sure what you're trying to do but I think you can skip the PHP and just use JavaScript and sessionStorage: https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Storage#sessionStorage or localStorage: https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Storage#localStorage (depending on how persistent you want the data to be)
For example if I want to listen for all clicks on 'a' tags and track whether those hrefs have been visited (and how many times)
$(document).ready(function() {
// store an empty object in session storage when the page loads
sessionStorage.visited = JSON.stringify({});
});
$('a').on('click', function() {
var storage = JSON.parse(sessionStorage.visited),
href = $(this).attr('href');
// when we get a click check to see if this has been clicked before
if (!storage[href]) {
// if not save it and set the count to 1
storage[href] = 1;
} else {
// otherwise increment the count
storage[href]++;
}
sessionStorage.visited = JSON.stringify(storage);
});
If you want to save the urls from your ajax calls the same principle applies but listen for the ajaxSuccess event and store the url from the request in the callback: http://api.jquery.com/ajaxSuccess/
This is my suggestion:
PHP + Javascript Implementation:
In PHP, use $_GET['curr'] and $_GET['cat'] to retrieve the arguements from the URL.
Use $_SESSION['curr'] = $_GET['curr']; to save them per the session.
On your Javascript/jQuery use var curr = "<?php echo $_SESSION['curr']; ?>" to make the PHP session variables available to your Javascript.
Basically the key to have a good PHP/Javascript persistent memory is that you can set PHP content into a Javascript variable by using:
var x = <?php echo '123';?>;
console.log(x); //output '123' to Javascript console
If you need to have a list of all visited urls, you can save them in a PHP array and transfer it to Javascript as well.
On PHP side:
if (!isset($_SESSION['visited'])) $_SESSION['visited'] = array();//initialize the array if doesn't exist
if (!inarray( $_SERVER['REQUEST_URI'], $_SESSION['visited']) { //check if current URL is not in array
$_SESSION['visited'][] = $_SERVER['REQUEST_URI'];//push it to the array
}
On Client side:
//this will convert the PHP array to a Javascript array using json_encode
var visited_urls= <?php echo json_encode($_SESSION['visited']); ?>;
Don't forget to use session_start() on every page you need the session variables.
Javascript Only Implementation:
Use localStorage and keep everything on the client side.
EDIT: Note that localStorage is only supported in IE8 and up, so if versions prior to IE8 must be supported, you will need to use Cookies instead of localStorage.
$(document).ready(function() {
var urls = JSON.parse(localStorage["visited"]) || [];//get the visited urls from local storage or initialize the array
if (urls.indexOf(document.URL) == -1) {//if current url does not exist in the array
urls.push(document.URL);//add it to the array
localStorage["visited"] = JSON.stringify(urls);//save a stringifyed version of the array to local storage
}
});
Hope this helps!
It's unclear what you want to achieve with this feature. You state:
Users can click around my app and apply different filters and scrolls through pages and I need some way of tracking this so that I always know what data has already been loaded for that users session.
What do you want to achieve with this, why isn't the browser's cache enough for you?
My idea for a solution would be to sync server session array with an object inside the Browser via some sort of WebSocket (http://en.wikipedia.org/wiki/WebSocket).
UPDATE2:
It is possible to use localStorage as cache storage as Abel Melquiades Callejo suggests and then read from it bypassing HTTP requests. I would choose what content to save to that cache differently, no server involved:
add a custom attribute data-* to every HTML element you want cached (http://html5doctor.com/html5-custom-data-attributes/);
make a querySelectorAll (https://developer.mozilla.org/en-US/docs/Web/API/Document.querySelectorAll) for all HTML elements with that attribute;
storing and retrieving documents from localStorage should be easy now, you need a convention for naming files for easy finding;
storing images implies doing a base64 transformation which increases the size of the data by 34% (image with 64kb will take 86kb in localStorage).
you need a way to find when data in localStorage is obsolete and you need to make requests to the server (perhaps another data-age attribute to specify when should it expire).
However, this localStorage solution is limited to a small amount of data, see this issue https://github.com/RemoteStorage/remoteStorage.js/issues/144. So, although I now see that what you are asking is possible, because of this size limitation to localStorage, I strongly recommend the solutions in my UPDATE1, below.
UPDATE1: The point is that caching mechanisms are incredibly complex. A better alternative would be to use the default browser caching mechanisms:
1. HTML5 cache manifest
Go offline with application cache
http://html5doctor.com/go-offline-with-application-cache/
LET’S TAKE THIS OFFLINE http://diveintohtml5.info/offline.html
Using the application cache
https://developer.mozilla.org/en/docs/HTML/Using_the_application_cache
A Beginner's Guide to Using the Application Cache http://www.html5rocks.com/en/tutorials/appcache/beginner/
2. Server's response headers to HTTP requests
Optimize caching - Leverage browser caching
https://developers.google.com/speed/docs/best-practices/caching#LeverageBrowserCaching
HTTP Caching FAQ https://developer.mozilla.org/en/docs/HTTP_Caching_FAQ
In my case :
I have created a form and in the form there is a button and a combo box that contains the data (Say it page A). When I click on the button, all I wanted was to call page B to perform a second process. The syntax for calling the page B is :
bb.pushScreen('PageB.htm', 'PageB', {'Key': MyComboValue});
How do I after page after page B called B will capture and get the value of the "MyComboValue" being sent from page A ??
Regards,
Bertho
Firstly, this is available in bbUI 0.94 (next branch) just to make sure you're running the right build.
Now, the object you pass to the new page is available in the ondomready, and onscreenready functions, so you would do something like this:
onscreenready: function(element, id, passed_object) { }
There are several ways to achieve this:
Use cookies to save the data. Wouldn't recommend it much.
Use localStorage. Works for newer browsers, some browsers won't be able to enjoy it.
Pass the values as querystring parameters when doing the change of url.
I would go with the third option myself. If you're only using JavaScript and you're not using any server side programming language:
Attach an event to the button so that when clicked, it fetches the data and generates a querystring. Then: top.location = "http://something/PageB.htm?" + querystring;
On the Page B, read the querystring (top.location.href) and parse it to get the querystring. Use the values of the querystring to set whatever you want on your page.
If you require code or if I misunderstood, please tell and I will check right away!
EDIT: I just realized you tagged your question as using blackberry-webworks. I have never worked with it and thus I have no idea if my solutions make sense on it. Try to specify it on your question too if possible, or in the title :)
How would I have a JavaScript action that may have some effects on the current page but would also change the URL in the browser so if the user hits reload or bookmark, then the new URL is used?
It would also be nice if the back button would reload the original URL.
I am trying to record JavaScript state in the URL.
If you want it to work in browsers that don't support history.pushState and history.popState yet, the "old" way is to set the fragment identifier, which won't cause a page reload.
The basic idea is to set the window.location.hash property to a value that contains whatever state information you need, then either use the window.onhashchange event, or for older browsers that don't support onhashchange (IE < 8, Firefox < 3.6), periodically check to see if the hash has changed (using setInterval for example) and update the page. You will also need to check the hash value on page load to set up the initial content.
If you're using jQuery there's a hashchange plugin that will use whichever method the browser supports. I'm sure there are plugins for other libraries as well.
One thing to be careful of is colliding with ids on the page, because the browser will scroll to any element with a matching id.
With HTML 5, use the history.pushState function. As an example:
<script type="text/javascript">
var stateObj = { foo: "bar" };
function change_my_url()
{
history.pushState(stateObj, "page 2", "bar.html");
}
var link = document.getElementById('click');
link.addEventListener('click', change_my_url, false);
</script>
and a href:
<a href="#" id='click'>Click to change url to bar.html</a>
If you want to change the URL without adding an entry to the back button list, use history.replaceState instead.
window.location.href contains the current URL. You can read from it, you can append to it, and you can replace it, which may cause a page reload.
If, as it sounds like, you want to record javascript state in the URL so it can be bookmarked, without reloading the page, append it to the current URL after a # and have a piece of javascript triggered by the onload event parse the current URL to see if it contains saved state.
If you use a ? instead of a #, you will force a reload of the page, but since you will parse the saved state on load this may not actually be a problem; and this will make the forward and back buttons work correctly as well.
I would strongly suspect this is not possible, because it would be an incredible security problem if it were. For example, I could make a page which looked like a bank login page, and make the URL in the address bar look just like the real bank!
Perhaps if you explain why you want to do this, folks might be able to suggest alternative approaches...
[Edit in 2011: Since I wrote this answer in 2008, more info has come to light regarding an HTML5 technique that allows the URL to be modified as long as it is from the same origin]
jQuery has a great plugin for changing browsers' URL, called jQuery-pusher.
JavaScript pushState and jQuery could be used together, like:
history.pushState(null, null, $(this).attr('href'));
Example:
$('a').click(function (event) {
// Prevent default click action
event.preventDefault();
// Detect if pushState is available
if(history.pushState) {
history.pushState(null, null, $(this).attr('href'));
}
return false;
});
Using only JavaScript history.pushState(), which changes the referrer, that gets used in the HTTP header for XMLHttpRequest objects created after you change the state.
Example:
window.history.pushState("object", "Your New Title", "/new-url");
The pushState() method:
pushState() takes three parameters: a state object, a title (which is currently ignored), and (optionally) a URL. Let's examine each of these three parameters in more detail:
state object — The state object is a JavaScript object which is associated with the new history entry created by pushState(). Whenever the user navigates to the new state, a popstate event is fired, and the state property of the event contains a copy of the history entry's state object.
The state object can be anything that can be serialized. Because Firefox saves state objects to the user's disk so they can be restored after the user restarts her browser, we impose a size limit of 640k characters on the serialized representation of a state object. If you pass a state object whose serialized representation is larger than this to pushState(), the method will throw an exception. If you need more space than this, you're encouraged to use sessionStorage and/or localStorage.
title — Firefox currently ignores this parameter, although it may use it in the future. Passing the empty string here should be safe against future changes to the method. Alternatively, you could pass a short title for the state to which you're moving.
URL — The new history entry's URL is given by this parameter. Note that the browser won't attempt to load this URL after a call to pushState(), but it might attempt to load the URL later, for instance after the user restarts her browser. The new URL does not need to be absolute; if it's relative, it's resolved relative to the current URL. The new URL must be of the same origin as the current URL; otherwise, pushState() will throw an exception. This parameter is optional; if it isn't specified, it's set to the document's current URL.
Browser security settings prevent people from modifying the displayed url directly. You could imagine the phishing vulnerabilities that would cause.
Only reliable way to change the url without changing pages is to use an internal link or hash. e.g.: http://site.com/page.html becomes http://site.com/page.html#item1 . This technique is often used in hijax(AJAX + preserve history).
When doing this I'll often just use links for the actions with the hash as the href, then add click events with jquery that use the requested hash to determine and delegate the action.
I hope that sets you on the right path.
There is a Yahoo YUI component (Browser History Manager) which can handle this: http://developer.yahoo.com/yui/history/
Facebook's photo gallery does this using a #hash in the URL. Here are some example URLs:
Before clicking 'next':
/photo.php?fbid=496429237507&set=a.218088072507.133423.681812507&pid=5887027&id=681812507
After clicking 'next':
/photo.php?fbid=496429237507&set=a.218088072507.133423.681812507&pid=5887027&id=681812507#!/photo.php?fbid=496435457507&set=a.218088072507.133423.681812507&pid=5887085&id=681812507
Note the hash-bang (#!) immediately followed by the new URL.
A more simple answer i present,
window.history.pushState(null, null, "/abc")
this will add /abc after the domain name in the browser URL. Just copy this code and paste it in the browser console and see the URL changing to "https://stackoverflow.com/abc"
There's a jquery plugin http://www.asual.com/jquery/address/
I think this is what you need.
What is working for me is - history.replaceState() function which is as follows -
history.replaceState(data,"Title of page"[,'url-of-the-page']);
This will not reload page, you can make use of it with event of javascript
I was wondering if it will posible as long as the parent path in the page is same, only something new is appended to it.
So like let's say the user is at the page: http://domain.com/site/page.html
Then the browser can let me do location.append = new.html
and the page becomes: http://domain.com/site/page.htmlnew.html and the browser does not change it.
Or just allow the person to change get parameter, so let's location.get = me=1&page=1.
So original page becomes http://domain.com/site/page.html?me=1&page=1 and it does not refresh.
The problem with # is that the data is not cached (at least I don't think so) when hash is changed. So it is like each time a new page is being loaded, whereas back- and forward buttons in a non-Ajax page are able to cache data and do not spend time on re-loading the data.
From what I saw, the Yahoo history thing already loads all of the data at once. It does not seem to be doing any Ajax requests. So when a div is used to handle different method overtime, that data is not stored for each history state.
my code is:
//change address bar
function setLocation(curLoc){
try {
history.pushState(null, null, curLoc);
return false;
} catch(e) {}
location.hash = '#' + curLoc;
}
and action:
setLocation('http://example.com/your-url-here');
and example
$(document).ready(function(){
$('nav li a').on('click', function(){
if($(this).hasClass('active')) {
} else {
setLocation($(this).attr('href'));
}
return false;
});
});
That's all :)
I've had success with:
location.hash="myValue";
It just adds #myValue to the current URL. If you need to trigger an event on page Load, you can use the same location.hash to check for the relevant value. Just remember to remove the # from the value returned by location.hash e.g.
var articleId = window.location.hash.replace("#","");