Using Vanilla Javascript, I need to write a script that console logs an array of every unique page path visited during a browser session.
Here is my starting point:
function() {
var pageUrl = window.location.href;
return pageUrl;
}
Session storage is a small (often 5MB) area of space on the user's computer which you can write to. When the session ends (e.g. the browser is closed) the session storage is cleared, and in this way it is different to local storage.
Lets imagine we keep a list of pages around in the session storage. Items are stored in session storage as key / value pairs of strings, and as such we need a key (think of this as a variable name that persists across pages) and a value. Initially, you may think that we can just store an array in such a data store, but the session storage API can only store strings. Therefore, we'll need to convert our array back and forward between a JSON representation as we use it.
When you need to get the list of pages, just grab it from session storage (it's important you get a fresh copy each time, as this accounts for e.g. visits in another tab while the current one remained open). You can do this like so:
function getListOfPages() {
let pages = [];
// If not null, then we have previously stored something --- load that in
if (sessionStorage.getItem("visited") != null) {
// Get the item, and convert it from a JSON string to an actual array
pages = JSON.parse(sessionStorage.getItem("visited"));
}
return pages;
}
And when we need to add a new page, we can then just update that list in storage. For example, we could use a function such as this one:
function addPageToList() {
// Use the get function we made before to get the most up-to-date list of pages
let pages = getListOfPages();
// Add the current page to the list
pages.push(window.location.href);
// Override the value stored in session storage, or add it if no such value exists
sessionStorage.setItem("visited", JSON.stringify(pages));
}
In your post, you mention you want to console.log all of the unique paths. To do this, you could replace the array in all of the above code with a set, as sets cannot contain duplicate items. However, do note that you can't convert a set to JSON (at least not directly / intuitively), so you may be best advised to do this step after obtaining the list of pages, e.g:
console.log(new Set(getListOfPages()));
Wrapping in a set will remove all of the duplicate items in the array, and then you can print out the set to the console as normal.
Related
I'm building a web app that lets the user curate a double-feature film showing. The user enters a title, a blurb, and two film titles. On a submit button a function is called that displays the user-submitted title, user-submitted blurb, and makes two separate API calls to retrieve movie information on each respective feature.
I'm trying to establish something of a favorites functionality that utilizes localStorage. Conceptualizing the solution, let alone implementing it may be my first mistake, so I'm open to alternative suggestions, but I believe the best way to do this is to capture each search field value (title, blurb, movie_1, movie_2), store these four string values in an object and then push that object to an array, placing each object into localStorage and then getting each object from localStorage later on with a button click.
I'm able to capture these items, store them in localStorage and dynamically generate buttons that when clicked populates the four search field values back into the respective search fields, allowing the user to click the submit button again which runs the api calls and displays all of the content (again: title, blurb, movie_1, movie_2).
My problem is looping through the objects and grabbing the different search field data values. I suppose the problem is in assigning a name or key to the different objects that I'm looping through in the array and then accessing the correct values from the appropriate button through localStorage. I seem to be setting the same localStorage object (or rewriting it) and accessing it over and over, as opposed to setting a new localStorage object and getItem'ing the right one.
I'll provide some code snippets below, but it might be easier to peruse my GitHub repo: https://github.com/mchellagnarls/double_feature
If you look at the repo, latest code is found in index_test.html and app_test.js, whereas a previous version without any of the broken favorite functionality is found in index.html and app.js.
Some code snippets:
// logic to capture search field values and to eventually display them as buttons
// empty array
var dfArray = [];
// object to hold each of the string values to populate the search fields
var doubleFeature = {
feature_1: movie_1,
feature_2: movie_2,
DFTitle: title,
DFBlurb: blurb
}
dfArray.push(doubleFeature);
for (var i = 0; i < dfArray.length; i++) {
localStorage.setItem("df", JSON.stringify(dfArray[i]));
var button = $("<button>");
button.addClass("df-favorite-button");
button.text(title);
$("#buttons-view").append(button);
}
$(document).on("click", ".df-favorite-button", function() {
event.preventDefault();
var savedDF = JSON.parse(localStorage.getItem("df"));
$("#movie-input-1").val(savedDF.feature_1);
$("#movie-input-2").val(savedDF.feature_2);
$("#df-title-input").val(savedDF.DFTitle);
$("#df-blurb-input").val(savedDF.DFBlurb);
})
Thanks for any help. I'm learning web development and I may be overcomplicating things or missing out on an easier way to think about it and/or solve it.
Instead of this loop:
for (var i = 0; i < dfArray.length; i++) {
localStorage.setItem("df", JSON.stringify(dfArray[i]));
var button = $("<button>");
button.addClass("df-favorite-button");
button.text(title);
$("#buttons-view").append(button);
}
I would place the whole array into local storage
localStorage.setItem("df", JSON.stringify(dfArray));
Also then you have to decide which object to get from array in click function
$(document).on("click", ".df-favorite-button", function() {
event.preventDefault();
var id = -1; // which one
var savedDF = (JSON.parse(localStorage.getItem("df")) || []).find(pr => pr.id === id);
$("#movie-input-1").val(savedDF.feature_1);
$("#movie-input-2").val(savedDF.feature_2);
$("#df-title-input").val(savedDF.DFTitle);
$("#df-blurb-input").val(savedDF.DFBlurb);
})
I have an array in my localStorage, product_categories, which holds numerous objects which contain a string and a nested array of objects (the inner objects are the products that belong to each category).
Since Appery's support couldn't help me figure out how to query an array of objects based on an attribute belonging to each object - I just made each object (product) belong to an array and categorize the products based on which array index they were.
Anyway, I am now trying to map an array of objects to my page's collapsible block, which I have done previously - but with a response from the online database.
Now, I am using a SEPARATE service and all I want it to do is grab the array of objects from the localStorage and map it to a collapsible block, or even a grid or list item, so that each index in the array auto-creates a new item.
However, it has not worked for anything I have tried. I tried to map it to every possible item that can have arrays mapped to it, and when I load the page - the item actually disappears, almost as if the array being mapped to it has a length of 0.
But, when I inspect the page in Chrome and look at the localStorage variable that is being used to hold the array of objects (and in-turn mapped to the page), the variable clearly has an array of objects in the same format as other localStorage variables being mapped to the page from storage.
If it helps, I am using a GenericService for pulling the localStorage variables and mapping them to the page. I am not using a custom implementation - all the service does is 'ON SUCCESS - MAPPING' and maps the storage to the page. However, like I said, this isn't working.
Since I have been here for a while but never actually posted or anything, I don't have the rep to post the images that may help in solving my problem; with that in mind, here is the link to the original Appery.io support page which contains corresponding images: https://getsatisfaction.com/apperyio/topics/mapping-localstorage-array-to-collapsible-wont-work-removes-collapsible-item
StackOverflow, I would really appreciate your assistance as I always seem to run into a language barrier when working with Appery.io's support.
*EDIT Your revisions are not useful for my post, as they are changing localStorage to local storage - but in the case of Appery, localStorage is correct syntax.
var products = offlineProductList(); // grab the returned JSON array of products from the function offlineProductList()
var UniqueCats = $.unique(products.map(function (d) {
return d.category // this will return every distinct category
}));
var product_categories = []; // create empty array to hold each category object and its respective products
for(var i=0;i<UniqueCats.length;i++){
// for every unique category...
var category_products = []; // create array to hold products
for(var j=0;j<products.length;j++){
// run through list of products
if(products[j].category == UniqueCats[i]){
// if the product's category is the same as the current indexed unique category, add to array
category_products.push(products[j]);
}
}
var category = {"category":UniqueCats[i],"category_products":category_products}; // create object for the category
product_categories.push(category); // add object to the categorical array
}
localStorage.setItem('product_categories', JSON.stringify(product_categories));
Above is the code used to create the 'array of objects', and below is an image showing the mapping
array of objects mapping to an appery page element
I would give you more but SO won't allow me to with my rep. So if you ask for more I might just 404 myself
So I figured it out myself - Appery actually offers the option for mapping expressions on events like 'Page Load' (for pages/panels etc) and 'Click' (for buttons and similar elements), and I was succesfully able to map localStorage arrays to list, collapsible, and grid elements from there. I guess my GenericService was set up inproperly - but for future users having issues of just mapping something from Storage to the page - just link a mapping expression from an element's event.
I need to store client side data temporarily. The data will be trashed on refresh or redirect. What is the best way to store the data?
using javascript by putting the data inside a variable
var data = {
a:"longstring",
b:"longstring",
c:"longstring",
}
or
putting the data inside html elements (as data-attribute inside div tags)
<ul>
<li data-duri="longstring"></li>
<li data-duri="longstring"></li>
<li data-duri="longstring"></li>
</ul>
The amount of data to temporarily store could get a lot because the data I need to store are image dataUri's and a user that does not refresh for the whole day could stack up maybe 500+ images with a size of 50kb-3mb. (I am unsure if that much data could crash the app because of too much memory consumption. . please correct me if I am wrong.)
What do you guys suggest is the most efficient way to keep the data?
I'd recommend storing in JavaScript and only updating the DOM when you actually want to display the image assuming all the image are not stored at the same time. Also note the browser will also store the image in its own memory when it is in the DOM.
Update: As comments have been added to the OP I believe you need to go back to customer requirements and design - caching 500 x 3MB images is unworkable - consider thumbnails etc? This answer only focuses on optimal client side caching if you really need to go that way...
Data URI efficiency
Data URIs use base64 which adds an overhead of around 33% representing binary data in ASCII.
Although base64 is required to update the DOM the overhead can be avoided by storing the data as binary strings and encoding and decoding using atob() and btoa() functions - as long as you drop references to the original data allowing it to be garbage collected.
var dataAsBase64 = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
var dataAsBinary = atob(dataAsBase64);
console.log(dataAsBinary.length + " vs " + dataAsBase64.length);
// use it later
$('.foo').attr("src", "data:image/png;base64," + btoa(dataAsBinary));
String memory efficiency
How much RAM does each character in ECMAScript/JavaScript string consume? suggests they take 2 bytes per character - although this is still could be browser dependent.
This could be avoided by using ArrayBuffer for 1-to-1 byte storage.
var arrayBuffer = new Uint8Array(dataAsBinary.length );
for (i = 0; i < dataAsBinary.length; i++) {
arrayBuffer[i] = dataAsBinary.charCodeAt(i);
}
// allow garbage collection
dataAsBase64 = undefined;
// use it later
dataAsBase64 = btoa(String.fromCharCode.apply(null, arrayBuffer));
$('.foo').attr("src", "data:image/png;base64," + btoa(dataAsBinary));
Disclaimer: Note all this add a lot of complexity and I'd only recommend such optimisation if you actually find a performance problem.
Alternative storage
Instead of using browser memory
local storage - limited, typically 10MB, certainly won't allow - 500 x 3MB without specific browser configuration.
Filesystem API - not yet widely supported, but ideal solution - can create temp files to offload to disk.
if you really want to loose the data on a refresh, just use a javascript hash/object var storage={} and you have a key->value store. If you would like to keep the data during the duration of the user visiting the page (until he closes the browser window), you could use sessionStorage or to persist the data undefinetly (or until the user deletes it), use localStorage or webSQL
putting data into the DOM (as a data-attribute or hidden fields etc) is not a good idea as the process for javascript to go into the DOM and pull that information out is very expensive (crossing borders between the javascript- and the DOM-world (the website structure) doesn't come cheap)
Using Javascript variable is the best way to store you temp data. You may consider to storing your data inside a DOM attribute only if the data is related to a specific DOM element.
About the performance, storing your data directly in a javascript variable will probably be faster since storing data in a DOM element would also involve javascript in addition to the DOM modifications. If the data isn't related to an existing DOM element, you'll also have to create a new element to store that value and make sure it isn't visible to the user.
The OP mentions a requirement for the data to be forcibly transient i.e. (if possible) unable to be saved locally on the client - at least that is how I read it.
If this type of data privacy is a firm requirement for an application, there are multiple considerations when dealing with a browser environment, I am unsure whether the images in question are to be displayed as images to the user, or where in relation to the client the source data of the images is coming from. If the data is coming into the browser over the network, you might do well (or better than the alternative, at least) to use a socket or other raw data connection rather than HTTP requests, and consider something like a "sentinel" value in the stream of bytes, to indicate boundaries of image data.
Once you have the bytes coming in, you could, I believe, (or soon will be able to) pass the data via a generator function into a typedArray via the iterator protocol, see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array
// From an iterable
var iterable = function*(){ yield* [1,2,3]; }();
var uint8 = new Uint8Array(iterable);
// Uint8Array[1, 2, 3]
And then perhaps integrate those arrays as private members of some class you use to manage their lifecycle? see:
https://www.nczonline.net/blog/2014/01/21/private-instance-members-with-weakmaps-in-javascript/
var Person = (function() {
var privateData = {},
privateId = 0;
function Person(name) {
Object.defineProperty(this, "_id", { value: privateId++ });
privateData[this._id] = {
name: name
};
}
Person.prototype.getName = function() {
return privateData[this._id].name;
};
return Person;
}());
I think you should be able to manage the size / wait problem to some extent with the generator method of creating the byte arrays as well, perhaps check for sane lengths, time passed on this iterator, etc.
A general set of ideas more than an answer, and none of which are my own authorship, but this seems to be appropriate to the question.
Why not used #Html.Hidden ?
#Html.Hidden("hId", ViewData["name"], new { #id = "hId" })
There are various ways to do this, depending upon your requirement:
1) We can make use of constant variables, create a file Constants.js and can be used to store data as
"KEY_NAME" : "someval"
eg:
var data = {
a:"longstring",
b:"longstring",
c:"longstring",
}
CLIENT_DATA = data;
Careful: This data will be lost if you refresh the screen, as all the variables memory is just released out.
2) Make use of cookieStore, using:
document.cookie = some val;
For reference :http://www.w3schools.com/js/tryit.asp?filename=tryjs_cookie_username
Careful: Cookie store data has an expiry period also has a data storage capacity https://stackoverflow.com/a/2096803/1904479.
Use: Consistent long time storage. But wont be recommended to store huge data
3) Using Local Storage:
localStorage.setItem("key","value");
localStorage.getItem("key");
Caution: This can be used to store value as key value pairs, as strings, you will not be able to store json arrays without stringify() them.
Reference:http://www.w3schools.com/html/tryit.asp?filename=tryhtml5_webstorage_local
4) Option is to write the data into a file
Reference: Writing a json object to a text file in javascript
I've made users to enter some values in a html page:
Here is the code:
$(function() {
$("#savesymptoms").bind(
"click",
function() {
var url = "notes.html?acne="
+ encodeURIComponent($("#acne").val())
+ "&back="
+ encodeURIComponent($("#bloat").val());
window.location.href = url;
});
});
And I've added an alert like this:
alert("values passed");
But I'm not sure that this is working. I've got some 10 values to pass to another page. Above code contains only two values. Can I use arrays to pass all 10 values? I don't want to use nomenclature used in my code, i.e., individual value passing.
P.S: acne and bloat are the ids of the fields where user enters the data. notes.html is the page where I want to pass the data.
In my opinion you can use sessionStorage in HTML5
For setting a value
sessionStorage.setItem('name', 'value');
For getting the value:
sessionStorage.getItem('name');
if you want to store the value permanently use localStorage.
Since you have almost 10 values push all those values in a JSON and store it either in sessionStorage/localStorage.
You can retrieve it else where and use as per your wish
Note:values stored will be in the form of string even if you push it as JSON.You will have to make it to JSON using Jquery functions
Alternative
You can store it as window.name
But this will be only valid till the window lasts/tab closed(same as sessionStorage)
I have an application which open several windows (with javascript) in the same domain.
I would like to share some javascript objects between these windows (an object which contains some configurations for example).
Is it possible to do this in javascript and how to do this ?
Thanks.
There are 2 possibilities: local storage and session storage
The session storage stores value for duration of the session, the value gets deleted when browser is closed and re-opened.
// Store value
sessionStorage.setItem('key', 'value');
//or
sessionStorage['key'] = value;
// Retrieve value
alert(sessionStorage.getItem('key'));
The local storage stores value beyond the duration of the session, the value can be retrievedeven after closing and re-opening the browser.
// Store value
localStorage.setItem('key', 'value');
//or
localStorage['key'] = value;
// Retrieve value
alert(localStorage.getItem('key'));
Use localStorage along with JSON to store your objects as strings:
Setting:
window.localStorage.setItem('yourKey', JSON.stringify(yourObject));
Getting:
var yourObject = JSON.parse(window.localStorage.getItem('yourKey'));
the localStorage data will be shared across all of your pages as long as they exist in the same domain.
If you used window.open() to create the second window, window.opener in the second window might give you access to the first window. See MDN.