One local storage JavaScript for fields on different pages - javascript

Three different web pages have three contenteditable areas each (content1, content2, and content3).
Each page links to one JavaScript which uses local storage to store the user's input and present it again on their return.
When I change the content on one page, it changes the content in the same editable area all three pages.
I want each page to be able to use the same script to save it's own data independently of the other pages.
I've tried adding page location (url) to the local storage key, to get each page to use the same script to store and retrieve it's own data, but I can't get it to work. Am new to JavaScript - would be grateful for any help. Thanks!
window.addEventListener('load', onLoad);
function onLoad() {
checkEdits();
}
// Get page location
var loc = encodeURIComponent(window.location.href);
// Add location to local storage key
function checkEdits() {
if (localStorage.userEdits1 != null) {
var userEdits1 = (loc + userEdits1);
document.getElementById('content1').innerHTML = localStorage.userEdits1;
}
if (localStorage.userEdits2 != null) {
var userEdits2 = (loc + userEdits2);
document.getElementById('content2').innerHTML = localStorage.userEdits2;
}
if (localStorage.userEdits3 != null) {
var userEdits3 = (loc + userEdits3);
document.getElementById('content3').innerHTML = localStorage.userEdits3;
}
};
document.onkeyup = function (e) {
e = e || window.event;
console.log(e.keyCode);
saveEdits();
};
function saveEdits() {
// Get editable elements
var editElem1 = document.getElementById('content1');
var editElem2 = document.getElementById('content2');
var editElem3 = document.getElementById('content3');
// Get edited elements contents
var userVersion1 = editElem1.innerHTML;
var userVersion2 = editElem2.innerHTML;
var userVersion3 = editElem3.innerHTML;
// Add page location to storage key
var userEdits1 = (loc + userEdits1);
var userEdits2 = (loc + userEdits2);
var userEdits3 = (loc + userEdits3);
// Save the content to local storage
localStorage.userEdits1 = userVersion1;
localStorage.userEdits2 = userVersion2;
localStorage.userEdits3 = userVersion3;
};
function clearLocal() {
if (confirm('Are you sure you want to clear your notes on this page?')) {
localStorage.setItem("userEdits1", "");
localStorage.setItem("userEdits2", "");
localStorage.setItem("userEdits3", "");
document.getElementById('content1').innerHTML = "";
document.getElementById('content2').innerHTML = "";
document.getElementById('content3').innerHTML = "";
alert('Notes cleared');
}
}

The actual problem of your script is this:
localStorage.userEdits1
To access the property of an object with a string (e.g. stored in a variable) you have to use the bracket notation:
locationStorage[userEdits1]
But I would propose a slightly more generic (and, imho, cleaner) solution...
Store the content of the editable elements in an object
var cache = {
<elementX id>: <content>,
<elementY id>: <content>,
<elementZ id>: <content>,
...
};
And then store this "cache" in the local storage with a page-specific key
localStorage.setItem(window.location.pathName, JSON.stringify(cache));
A possible implementation could be:
window.addEventListener('load', checkEdits);
getContentEditables().forEach(function(editable) {
// This prevents the function to execute on every keyup event
// Instead it will only be executed 100ms after the last keyup
var debouncedFunc = debounce(100, function(e) {
saveEdits();
});
editable.addEventListener("keyup", debouncedFunc);
});
function checkEdits() {
var cache = localStorage.getItem(window.location.pathName);
if (cache !== null) {
cache = JSON.parse(cache);
Object.keys(cache)
.forEach(function(key) {
var element = document.getElementById(key);
if (element !== null) {
element.innerHTML = cache[key];
}
});
}
}
function saveEdits() {
var cache = {};
getContentEditables().forEach(function(element) {
cache[element.id] = element.innerHTML;
});
localStorage.setItem(window.location.pathName, JSON.stringify(cache));
};
function clearLocal() {
if (confirm('Are you sure you want to clear your notes on this page?')) {
localStorage.removeItem(window.location.pathName);
getContentEditables().forEach(function(element) {
element.innerHTML = "";
});
alert('Notes cleared');
}
}
// helper
function getContentEditables() {
var elements = [];
document.querySelectorAll("[contenteditable]")
.forEach(function(element) {
if (element.id) {
elements.push(element);
}
});
return elements;
}
function debounce(timeout, func) {
var timeoutId;
return function() {
var that = this,
args = arguments;
clearTimeout(timeoutId);
timeoutId = setTimeout(function() {
func.apply(that, args);
}, timeout);
}
}

Use
localStorage[userEdits1]
Instead of
localStorage.userEdits1

Related

Trying to use sessionStorage in javascript

I'm trying to show a popup window only once per session. In order to achieve that I'm using sessionStorage function. however, the popup is showing up whenever I reload the web page. Can you please let me know where I'm making the mistake.
dialogue = new IMu.App.Dialogue();
dialogue.setHtmlMessage(IMu.string('dialogue-heading'));
dialogue.show({ showDetails: true });
window.sessionStorage.setItem('message','true');
var is_dialogue = window.sessionStorage.getItem('message');
if (is_dialogue != 'true')
{
dialogue.show();
}
Below is the show function
show: function(options, callback)
{
if (typeof(options) == 'function')
{
callback = options;
options = undefined;
}
var test = jQuery('body').children('div.imu-dialogue');
jQuery('body').children('div.imu-dialogue').remove();
var owner = self.owner = jQuery('body').child('div', 'imu-dialogue');
var box = owner.child('div', 'box');
var message = box.child('div', 'message');
if (this.message)
{
var value = this.message.value;
var method = this.message.type;
message[method](value);
}
if (self.details)
{
var details = box.child('div', 'details');
for (var i in self.details)
{
if (! self.details.hasOwnProperty(i))
continue;
var detail = details.child('div', 'detail');
var value = self.details[i].value;
var method = self.details[i].type || 'text';
detail[method](value);
}
var show = box.child('div', 'show-details');
if (! options || options.showDetails !== true)
{
show.text('Details');
details.hide();
}
else
{
details.show();
show.text('I agree');
}
show.on('click', function()
{
self.owner.remove();
});
}
You've got your logic backwards. You need to check if the sessionStorage item has been set first. If it has not been set, show the dialog and then set it.
if (!sessionStorage.getItem('notified')) {
let dialogue = new IMu.App.Dialogue();
dialogue.setHtmlMessage(IMu.string('dialogue-heading'));
dialogue.show({ showDetails: true });
sessionStorage.setItem('notified', 'true');
}
There is no need to check the value of the stored property. Simply testing for it's existence is enough.
Forget the dialogue.show();
If you try in a new tab of your browser the following code:
window.sessionStorage.setItem('message','true');
is_dialogue = window.sessionStorage.getItem('message');
is_dialogue != 'true' // false
refresh the page and run
is_dialogue = window.sessionStorage.getItem('message');
is_dialogue != 'true' // false
You may have something in your code whick clean session storage on mounted or on created ?

Can I watch for changes to datalayer variables in Google Tag Manager?

I am writing a Javascript function that will be a tag in Google Tag Manager.
It is loaded on a SPA which I have minimal control over.
Whenever a user clicks, I use the GTM functionality to push some data to the datalayer, e.g.:
var someEventIsTriggered = function(e) {
var target = $('input#watched');
// Trigger a generic "gtm.click" event on every click
dataLayer.push({
"event": "gtm.customEvent",
"gtm.customWatchedVariable": target.val()
});
};
Every time this is triggered, it will push a new event to the datalayer, and updated the value of gtm.customWatchedVariable. What I now want to check is if the current gtm.customWatchedVariable is different from the last gtm.customWatchedVariable, and then fire a Trigger in GTM when it changes.
How can I do this?
This JS is checking if last gtm.customWatchedVariable variables in datalayer object is different:
var someEventIsTriggered = function(e) {
var target = $('input#watched');
dataLayer.push({
"event": "gtm.customEvent",
"gtm.customWatchedVariable": target.val()
});
var customWatcherVar = dataLayer.filter(function(e){ return typeof(e["gtm.customWatchedVariable"]) != 'undefined';});
var prevDatalayer = customWatcherVar[customWatcherVar.length-2];
var newDatalayer = customWatcherVar[customWatcherVar.length-1];
var prevVal = null;
var newVal = null;
if (prevDatalayer!=null)
{
prevVal = prevDatalayer["gtm.customWatchedVariable"];
}
if (newDatalayer!=null)
{
newVal = newDatalayer["gtm.customWatchedVariable"];
}
if (prevVal != newVal)
{
// Push another datalayer, because gtm.customWatchedVariable changed
}
};
Thanks to #victor-leontyev, for pointing me towards the answer.
I didn't realise you could treat the dataLayer object like any other array. So my code now looks like this:
var someEventIsTriggered = function(e) {
var target = $('input#watched');
var lastEvent = dataLayer
.filter(function (e) { return e.event === 'gtm.customEvent'; })
.pop();
var lastValue = lastEvent instanceof Object
? lastEvent["gtm.customWatchedVariable"]
: false;
// Trigger a generic "gtm.click" event on every click
dataLayer.push({
"event": "gtm.customEvent",
"gtm.customWatchedVariable": target.val()
});
if (lastValue !== target.val()) {
// Do the thing.
}
};
Thanks!

How to use localStorage to save variable with random value

I'm trying to use localStorage to save a variable with value generated randomly from an array in a JavaScript file, and pass it to another HTML file. However, the value in Javascript file (Random_Msg) and the value in HTML file (Random_Msg1) are not the same, means it's not saved, instead it generated randomly another value.
These are the code to generate variable and save in localStorage:
function CreateRandomMsg(){
var RandomMsg = Msgs_Arr[Math.floor(Math.random()* Msgs_Arr.length)];
return RandomMsg;
}
var Random_Msg = CreateRandomMsg();
function alertMsg(){
alert(Random_Msg);
}
window.localStorage.setItem("Random_Msg1",Random_Msg);
In my HTML file, I just retrieved the variable first:
var Random_Msg1 = window.localStorage.getItem("Random_Msg1");
And use it in if statement:
if (Random_Msg1 == Msgs_Arr[0] || Random_Msg1 == Msgs_Arr[1]){
value = facesDet.photos[0].tags[0].attributes.glasses.value;
confidence = facesDet.photos[0].tags[0].attributes.glasses.confidence;
} else if (Random_Msg1 == Msgs_Arr[2] || Random_Msg1 == Msgs_Arr[3]) {
value = facesDet.photos[0].tags[0].attributes.smiling.value;
confidence = facesDet.photos[0].tags[0].attributes.smiling.confidence;
};
You can check on pageload if value for necessary key exists in localStorage. If value is missing, then compute and save new value. You can also have an extra event on which you can override value of this key, but this will be a user action.
Note: Stack Overflow does not give access to localStorage and any testing should be done on JSFiddle.
Sample
JSFiddle
function computeRandomValue() {
var data = ["test", "test1", "test2", "test3", "test4"];
var index = Math.floor(Math.random() * 10) % data.length;
return data[index];
}
function setToLocalStorage(newVal) {
var lsKey = "_lsTest";
localStorage.setItem(lsKey, newVal);
}
function getFromLocalStorage() {
var lsKey = "_lsTest";
return localStorage.getItem(lsKey);
}
function initializePage() {
var _val = getFromLocalStorage();
if (!(_val && _val.trim().length > 0)) {
_val = computeAndSaveNewValue();
}
printValue(_val, "lblResult");
}
function computeAndSaveNewValue() {
var newVal = computeRandomValue();
setToLocalStorage(newVal);
printValue(newVal);
return newVal;
}
function printValue(value) {
var id = "lblResult";
document.getElementById(id).innerHTML = value;
}
(function() {
window.onload = initializePage;
})()
<p id="lblResult"></p>
<button onclick="computeAndSaveNewValue()">Save new Value</button>

Can't update javaScript global variable

Here I have global variable userId, and i want to update it inside signInUserFunction(), to use is in other function. I have tried to define it using var, window, But all these didn't help. This variable doesn't update. As i see its about AJAX async. So, what can i do with it?
And yes, I know that its not good to make authentication with JS, I am quite new to it. So, I am just creating random methods to improve.
var userId = 1;
function signInUser() {
$.getJSON('http://localhost:8887/JAXRSService/webresources/generic/getAllUsers', function(data) {
var items = [];
var i = 0;
$.each(data, function(firstname, value) {
var str = JSON.stringify(value);
data = JSON.parse(str);
var innerId;
for (p in data) {
innerId = data[p].id;
if ($('#nameSignIn').val() == data[p].first_name && $('#passwordSignIn').val() == data[p].password) { //
userId = innerId;
window.location.href = "content.html";
break;
} else {
i++;
if (i == data.length) {
alert("Ощибка в логине или пароле!")
}
}
}
});
});
}
How are you determining whether or not it has been set? It looks like immediately after you set it, you navigate to a different page. When you get to that page, you will have an entirely new window.
Try alerting the value before navigating away.
EDITED: Here is how you could pass it to the other page (but you shouldn't do this in a real app)
window.userId=innerId;
alert(window.userId);
//this isn't a very secure way to do this. I DON'T recommend this
window.location.href = "content.html?id=" + innerId ;
Then in the other page, you could access it off the document.location:
alert(document.location.toString().split("?id=")[1]);
After reading my comments, you may want to try this:
var userId = 1;
function signInUser(){
$.getJSON('http://localhost:8887/JAXRSService/webresources/generic/getAllUsers', function(data){
var items = [], actors = data.Actors, l = 0;
$.each(actors, function(i, o){
l++;
if($('#nameSignIn').val() === o.first_name && $('#passwordSignIn').val() === o.password){
userId = o.id;
// this will redirect before any other code runs -> location = 'content.html';
if(l === actors.length){
alert('End of Loop');
}
}
});
});
}
signInUser();
I would not store sensitive data in JSON such as passwords. Use a database. There is no need to get all the data at the same time either.
Using the idea #mcgraphix proposed (and giving you the same warning...this would certainly not be the way to transfer data like this in a production environment), here is one way to do it:
function signInUser() {
var url = 'http://localhost:8887/JAXRSService/webresources/generic/getAllUsers';
var userId;
$.getJSON(url, function(data) {
$.each(data.Actors, function(index, actor) {
// Cache the values of the #nameSignIn and #passwordSignIn elements
var name = $('#nameSignIn').val();
var password = $('#passwordSignIn').val();
if (actor.first_name === name && actor.password === password) {
// We have found the correct actor.
// Extract its ID and assign it to userId.
userId = actor.id;
window.location.href = "content.html?userId=" + userId;
}
});
// This alert should only be reached if none of the actor objects
// has a name and password that matches your input box values.
alert("Ощибка в логине или пароле!");
});
}
// On the next page...
// Top answer from http://stackoverflow.com/questions/2090551/parse-query-string-in-javascript
// This approach can handle URLs with more than one query parameter,
// which you may potentially add in the future.
function getQueryVariable(variable) {
var query = window.location.search.substring(1);
var vars = query.split('&');
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split('=');
if (decodeURIComponent(pair[0]) == variable) {
return decodeURIComponent(pair[1]);
}
}
console.log('Query variable %s not found', variable);
}
var userId = getQueryVariable('userId');
Thanks you for help.Ended it all with usage of:
sessionStorage.getItem('label')
sessionStorage.setItem('label', 'value')

IndexedDB via Lawnchair gets larger with every save

I am using Lawnchair.js on a mobile app I am building at work targeting iOS, Android, and Windows phones. My question is I have a relatively simple function(see below), that reads data from an object and saves it in the indexeddb database. It's about 4MB of data and on the first go round when I inspect in Internet explorer(via internet options), I can see the database is about 7MB. If I reload the page and re-run the same function with the same data, it increases to 14MB and then 20MB. Im using the same keys so my understanding is that this should just update the record but it's almost as if it's just inserting all new records every time. I have also had similar behavior using Lawnchair on mobile safari using websql adapter. Has anyone seen this before or have any suggestions as to why this might be ??.
The following code is from a function I am using to populate the database.
populateDatabase: function(database,callback) {
'use strict';
var key;
try {
for(key in MasterData){
if(MasterData.hasOwnProperty(key)){
var itemInfo = DataConfig.checkForDataUpdates[DataConfig.keyMap[key]];
database.save({key:itemInfo["name"],hash:itemInfo["version"],url:itemInfo["url"],data:MasterData[key]});
}
}
callback(true);
} catch(e){
callback(false);
}
}
MasterData is the large data file and itemInfo contains the key name, a hash that is later used to check an api for updates, and the relative url of where to update from. After I create the database I pass it into this function and then pass back true if the inserts are successful and false otherwise.
As previously mentioned, I have seen similar issues in iOS where calling database.save() was allocating a lot of memory but not releasing it and eventually causing a crash if it populated the database and then tried to update some records. Removing Lawnchair from the equation has kept it from crashing but it is still allocating a lot of memory when saving data. Not sure if this is normal for persistent storage on mobile devices, a bug in Lawnchair, or me being a noob and doing something terribly wrong but I could use some pointers on this as well as why indexeddb just keeps getting larger and larger on every save (at least during initial testing in IE10)??
EDIT: Source Code for indexed-db adapter is here:
https://github.com/brianleroux/lawnchair/blob/master/src/adapters/indexed-db.js
and here is the code for the save function I am using:
save:function(obj, callback) {
var self = this;
if(!this.store) {
this.waiting.push(function() {
this.save(obj, callback);
});
return;
}
var objs = (this.isArray(obj) ? obj : [obj]).map(function(o){if(!o.key) { o.key = self.uuid()} return o})
var win = function (e) {
if (callback) { self.lambda(callback).call(self, self.isArray(obj) ? objs : objs[0] ) }
};
var trans = this.db.transaction(this.record, READ_WRITE);
var store = trans.objectStore(this.record);
for (var i = 0; i < objs.length; i++) {
var o = objs[i];
store.put(o, o.key);
}
store.transaction.oncomplete = win;
store.transaction.onabort = fail;
return this;
},
When Creating a new instance, Lawnchair uses the init function from the indexed-db adapter which is the following.
init:function(options, callback) {
this.idb = getIDB();
this.waiting = [];
this.useAutoIncrement = useAutoIncrement();
var request = this.idb.open(this.name, STORE_VERSION);
var self = this;
var cb = self.fn(self.name, callback);
if (cb && typeof cb != 'function') throw 'callback not valid';
var win = function() {
// manually clean up event handlers on request; this helps on chrome
request.onupgradeneeded = request.onsuccess = request.error = null;
if(cb) return cb.call(self, self);
};
var upgrade = function(from, to) {
// don't try to migrate dbs, just recreate
try {
self.db.deleteObjectStore('teststore'); // old adapter
} catch (e1) { /* ignore */ }
try {
self.db.deleteObjectStore(self.record);
} catch (e2) { /* ignore */ }
// ok, create object store.
var params = {};
if (self.useAutoIncrement) { params.autoIncrement = true; }
self.db.createObjectStore(self.record, params);
self.store = true;
};
request.onupgradeneeded = function(event) {
self.db = request.result;
self.transaction = request.transaction;
upgrade(event.oldVersion, event.newVersion);
// will end up in onsuccess callback
};
request.onsuccess = function(event) {
self.db = event.target.result;
if(self.db.version != (''+STORE_VERSION)) {
// DEPRECATED API: modern implementations will fire the
// upgradeneeded event instead.
var oldVersion = self.db.version;
var setVrequest = self.db.setVersion(''+STORE_VERSION);
// onsuccess is the only place we can create Object Stores
setVrequest.onsuccess = function(event) {
var transaction = setVrequest.result;
setVrequest.onsuccess = setVrequest.onerror = null;
// can't upgrade w/o versionchange transaction.
upgrade(oldVersion, STORE_VERSION);
transaction.oncomplete = function() {
for (var i = 0; i < self.waiting.length; i++) {
self.waiting[i].call(self);
}
self.waiting = [];
win();
};
};
setVrequest.onerror = function(e) {
setVrequest.onsuccess = setVrequest.onerror = null;
console.error("Failed to create objectstore " + e);
fail(e);
};
} else {
self.store = true;
for (var i = 0; i < self.waiting.length; i++) {
self.waiting[i].call(self);
}
self.waiting = [];
win();
}
}
request.onerror = function(ev) {
if (request.errorCode === getIDBDatabaseException().VERSION_ERR) {
// xxx blow it away
self.idb.deleteDatabase(self.name);
// try it again.
return self.init(options, callback);
}
console.error('Failed to open database');
};
},
I think you keep adding data instead of updating the present data.
Can you provide some more information about the configuration of the store. Are you using an inline or external key? If it's an internal what is the keypath.

Categories

Resources