loadItem(); with localStorage (broken) - javascript

I have two function to saveItem() and other to loadItem(); but I need see my items "when I Refresh the Page", I am using localStorage to save data doing of this a JSON.
var input = document.getElementById('input');
function newItem(list, itemText){
var item = document.createElement('li');
item.className = 'item';
item.innerText = itemText;
list.appendChild(item);
saveItem();
}
input.onkeyup = function(evt){
var key = evt.keyCode || evt.whitch;
if(key == 13){
itemText = input.value;
console.log('createITem');
if(!itemText || itemText == '' || itemText == ' '){
return false;
}
newItem(document.getElementById('ul'), itemText);
}
}
function saveItem(){
var items = document.querySelector('li.item');
var data = Array.prototype.map.call(items, function(item){
return [item.innerHTML];
});
localStorage.setItem('data', JSON.stringify(data));
}
function loadItem(){
var items = JSON.parse(localStorage.getItem('data'));
if(!items){
return;
}
Array.prototype.map.call(items, function(item){
return newItem(document.getElementById('content-memo'), item[0]);
});
}
loadItem();

Are you sure saveItem is working? Your code shows you calling loadItem, but it doesn't show you calling saveItem. In any case, that's where your problem is.
If you open your Dev Tools pane and inspect localStorage (or from the console, see if localStorage.data is defined), you should see if it's working properly. If not, then of course loadItem won't work as expected.
In order to map all li.items, you have to change the line from:
var items = document.querySelector('li.item');
To this:
var items = document.querySelectorAll('li.item');
querySelector will only return the first result as a DOM object, and you can't call Array.prototype.map on it. You need an array-like object. queryItemSelectorAll gives you that.
As it stands, the Array.prototype.map call in saveItem returns an empty array. So that's what gets set in localStorage.data - and thus what gets returned to the map function in loadItem.
Aside from that, are you having other troubles?

Related

Removing local storage item with vanilla javascript

I'm working on a simple to-do list with vanilla js. I've managed to add the input to local storage, but have not been able to add the style changes(check strike through) to local storage, nor can I figure out how to remove one item at a time from storage. I have been able to clear all, just unable to remove each item separately. Below is my code, any advice is greatly appreciated.
//local storage setup
let saved = window.localStorage.getItem(input.value);
if (saved) {
list.innerHTML = saved;
}
//handle input submit
function handleSubmitForm(e) {
e.preventDefault();
let input = document.querySelector('input');
if (input.value != '') {
addTodo(input.value);
}
input.value = '';
window.localStorage.setItem(input.value, list.innerHTML);
}
//check off todo
function checkTodo(e) {
let item = e.target.parentNode;
if (item.style.textDecoration == 'line-through') {
item.style.textDecoration = 'none';
} else {
item.style.textDecoration = 'line-through';
}
window.localStorage.setItem(item);
}
//delete todo
function deleteTodo(e) {
let item = e.target.parentNode;
item.addEventListener('transitionend', function () {
item.remove();
});
item.classList.add('todo-list-item-fall');
window.localStorage.removeItem(item);
}
JavaScript Storage is a key-value pair. Just use a string-based key so you can remove, edit or read it easily.
// Set todo item
localStorage.setItem("todo1", "Stand-up meeting 9.15am");
// Read todo item
localStorage.getItem("todo1");
// Delete todo item
localStorage.removeItem("todo1");
It's better if you can save it as a JSON string because you can mark it as completed without delete, so you can find completed tasks too.
// Saving todo item as a JSON string
localStorage.setItem("todo1", JSON.stringify({ text: "Stand-up meeting 9.15am", completed: false }));
// Read it
const todo = JSON.parse(localStorage.getItem("todo1"));
// You can read the text
console.log(todo.text);
// Also you can mark it as completed and save it back
todo.completed = true;
localStorage.setItem("todo1", JSON.stringify(todo));
Storing object in localStorage is a tricky job.
Everything you store in the local or session storage is of type string
you can create an object like
item = {
value : ANY_VALUE
}
and save it in your localStorage using JSON.stringify
localStorage.setItem(`item`,JSON.stringify(item))
now when you want to update the item just update the object and again set using the ablove syntax
To access the saved item from the local storage use JSON.parse
yourItemObject = JSON.parse(localStorage.getItem())```
You can access values now using yourItemObject .value
It appears you're passing the whole HTML element (it passed as an object) inside the removeItem function. you need to pass the key instead.
try localStorage.removeItem(item.innerText);
If you are working with lists in localStorage. I would use something like this basic example:
function addTodo(key, item){
var list = getTodo(key);
list.push(item);
localStorage.setItem(key, JSON.stringify(list) );
}
function getTodo(key){
try{
var rawList = localStorage.getItem(key);
return JSON.parse(rawList) || [];
}
catch(e){
return [];
}
}
function removeTodo(key, id){
var list = getTodo(key);
var newlist = list.filter( function(item){
return item.id != id;
});
localStorage.setItem(key, JSON.stringify(newlist) )
}
function emptyTodo(key){
localStorage.removeItem(key);
}
addTodo('list', {
id: 1,
text: 'do shopping'
});
addTodo('list', {
id: 2,
text: 'study'
});
console.log( getTodo('list') );
removeTodo('list', 1);
console.log( getTodo('list') )
emptyTodo('list');

Storing items as cookies, works perfectly fine on a normal browser, but on a native device browser there are implications

I am creating a website for a client at the moment, we decided an easy way to store "items" which will be passed down to a subdomain from the root would be to store them as cookies. This works perfectly fine in a normal browser, yet when I tested it on a native device browser it didn't work as smoothly. I am wondering where some of these problems may have been coming from and hoping you wonderful developers can lend a man a hand.
The idea is that on the frontend when a "Your Order" side drawer is pressed, a function runs grabbing the cookies and then sorts them into their specified content area's -> Downloadable Content, Requested Material and Bespoke Content. I have created two separate functions for this, one that was the original working piece and another more tailored and "good practice".
Tried having the "Value" of the cookie containing the values that need to be stored such as, [itemname],[itemlocation], [itemdescription], [itemtype].
The second function stores the item data in an object, the object is then JSON.stringified and iterated over in a for loop. This is then taken out of a string with JSON.parse() and further iterated over in an .each() iterating over the index(key) and val(value).
FIRST FUNCTION:
$('section#review-downloads a.toggle-btn').bind('click tap', function() {
let cookies;
let itemSplit;
var section = $('section#review-downloads');
if(section.hasClass('active')) {
section.removeClass('active');
setTimeout(function() {
$('section#review-downloads .selected-items div').find('p').remove();
}, 900);
} else {
section.addClass('active');
$.each(document.cookie.split(';'), function() {
cookies = this.split('=');
let trimId = cookies[0].trim();
vals = cookies[1].replace(/[\])}[{(]/g, '');
if(!(cookies[0] === "envFilter")) {
$.each(vals.split('[ ]'),function() {
itemSplit = this.split(',');
let itemId = trimId;
let itemName = itemSplit[0];
let itemUrl = itemSplit[1];
let itemType = itemSplit[2];
let itemDesc = itemSplit[3];
if(itemType === ' Downloadable Content ') {
$('<p id="selected-item-'+itemId+'"><strong>'+itemName+'</strong>'+itemDesc+'</p>').appendTo('section#review-downloads .review-container .selected-items .downloadable-content');
} else if (itemType === ' Requested Materials ') {
$('<p id="selected-item"><strong>'+itemName+'</strong>'+itemDesc+'</p>').appendTo('section#review-downloads .review-container .selected-items .requested-material');
} else if (itemType === ' Bespoke Content ') {
$('<p id="selected-item"><strong>'+itemName+'</strong>'+itemDesc+'</p>').appendTo('section#review-downloads .review-container .selected-items .bespoke-content');
}
});
};
});
}
return false;
});
THE SECOND FUNCTION (best practice)
$('div.support-item-wrapper div.order-add').bind('click tap', function() {
let id = $(this).data('id');
let name = $(this).data('title');
let file = $(this).data('file');
let type = $(this).data('type');
let desc = $(this).data('description').replace(/(\r\n|\n|\r)/gm, "");
let url = $(this).data('url');
let cookieVal = {
name: name,
file: file,
type: type,
desc: desc,
url: url
};
let string = JSON.stringify(cookieVal);
setCookie('product-'+id, string, 1);
});
$('section#review-downloads a.toggle-btn').bind('click tap', function() {
var section = $('section#review-downloads');
if(section.hasClass('active')) {
section.removeClass('active');
} else {
section.addClass('active');
let decoded_user_product;
cookie_values = document.cookie.split(';');
for(i = 0; i < cookie_values.length; i++) {
cookie_split = cookie_values[i].split("=");
cookie_key = cookie_split[0].trim();
cookie_value = cookie_split[1].trim();
// console.log(cookie_value);
if(cookie_key != "envFilter") {
decoded_user_product = JSON.parse(cookie_value);
}
$.each(decoded_user_product, function(index, val) {
// console.log("index:" + index + "& val:" + val);
if(index === "name") {
console.log(val);
} else if (index === "type") {
console.log(val);
} else if (index === "desc") {
console.log(val);
}
});
}
// console.log(decoded_user_product);
};
});
On Desktop, the results are perfectly fine. Each item is easily console.log()'able and has been easily sorted in the FIRST FUNCTION.
On Mobile, the same results were as to be expected. But after realising it hadn't worked I used chrome://inspect along with a lot of console.logs to come to the conclusion that I may be too inexperienced to understand what parts of my code are unable to run on a native browser.

Protractor test hangs when clicking on element

I have been trying to write a protractor test that selects an item from a custom dropdown menu. The only problem is that when it tries to click an element other than the last one in the list it hangs and timesout. When I remove the click() method invocation it seems to work fine. Since all these calls are done asynchronously I also don't see a way of stopping the loop when it finds the element. My code looks like this:
var it = null;
for(var i = 1; i <= totalNumberOfAccounts; i++) {
var listItemLocator = '//div[#id="payment-accounts"]/div/ul/li[' + i + ']/label/div/div[2]/div[2]/span[2]';
var item = browser.driver.findElement(protractor.By.xpath(listItemLocator));
item.getText().then(function(value) {
if(value === accountNumber) {
it = item;
}
console.log(value);
})
.then(function clickOption() {
console.log('Clicking...');
if (it) {
console.log('Clicking desired item');
it.click();
console.log('Clicked..');
}
})
}
I also tried this approach:
this.selectRichSelectOption = function (selector, item) {
var selectList = browser.driver.findElement(selector);
selectList.click();
var desiredOption = '';
var i = 1;
selectList.findElements(protractor.By.tagName('li'))
.then(function findMatchingOption(options) {
console.log(options);
options.some(function (option) {
console.log('Option:');
console.log(option);
var listItemLocator = '//div[#id="payment-accounts"]/div/ul/li[' + i + ']/label/div/div[2]/div[2]/span[2]';
console.log(listItemLocator);
var element = option.findElement(protractor.By.xpath('//label/div/div[2]/div[2]/span[2]'));
console.log('Element:');
console.log(element);
i++;
element.getText().then(function (value) {
console.log('Value: ' + value);
console.log('Item:');
console.log(item);
if (item === value) {
console.log('Found option..');
desiredOption = option;
return true;
}
return false;
});
});
})
.then(function clickOption() {
console.log('Click option');
console.log(desiredOption);
if (desiredOption) {
console.log('About to click..');
desiredOption.click();
}
});
};
The result of this one is even more strange. Now all of a sudden the getText() method invocation returns an empty String. But when I try to retrieve the e.g. the class attribute I get the correct value back. Where did the Text value go?
Can somebody please help me out?
This seems to be an issue with page load. After you select, the page does not load completely.
Try using a browser.sleep(timeInMs);
try using node 8+'s async functions such as await. I went through this headache and it was solved by awaiting for certain things to appear or have certain attributes.
await browser.wait(EC.presenceOf(element(by.xpath('path leading to element based off attribute'))))
Good luck

Storing Json in localStorage

I would like to store search results as cache in localStorage.
I would like to store all the cache as one localStorage value:
localStorage.getItem('search-cache')
Inside it I would like to have JSON object which I can add properties and retreive them.
Unfortunately it doesn't work and the localStorage is not updated with the json results (its value keep being '{}').
I am not a javascript proffesional so please guide me how to do it well.
Here is the current code to cache results:
var query = $(this).val();
var cache = JSON.parse(localStorage.getItem('search-cache'));
if (cache == null) {
cache = '[{}]';
}
if (cache[query] == null) {
$.getJSON('/api/guides/search?query=' + query, function (data) {
$.each(data, function (index, guide) {
$('#results').append('<li class="result-item">' + guide.Name + '</li>');
});
cache[query] = data;
localStorage.setItem('search-cache', JSON.stringify(cache));
});
}
else {
$.each(JSON.parse(localStorage.getItem('search-cache')[query]), function (index, guide) {
$('#results').append('<li class="result-item">' + guide.Name + '</li>');
});
}
You've got some holes in your logic.
var cache = JSON.parse(localStorage.getItem("..."));
if (cache == null) { cache = "[{}]"; }
Well, if the item DID exist, you've set cache to be equal to that object.
Otherwise, you've set cache to be equal to the string "[{}]".
Instead of thinking about how you're going to build your localstorage, think about how you're going to build your result list.
var cache_json = localStorage.getItem("search-cache"),
search_cache = JSON.parse(cache_json) || {};
var query = $("...").value(); // or whatever
search_cache[query] = search_cache[query] || { results : [] };
var list = $(......)
list.each(function () {
search_cache[query].results.push( /* whatever you want in your array */ );
});
cache_json = JSON.stringify(search_cache);
localStorage.setItem("search-cache", query_json);
Because, in case of your item search-cache is not defined, the initialization of your cache variable is not right.
You should initialize your array like this :
if (cache == null) {
cache = [];
cache[query] = null;
}
To meet the condition when testing
if (cache[query] == null)
however, you need to test it like this :
if(typeof cache[query] == 'undefined')
cache is an object and not an array, initialize like cache = {}
Rest of the code seems correct.

Issue with Javascript Variable Scope

I have a variable mutedUser which I would like to have persist to another function. I am having a bit of trouble with the variable persisting outside the click event. What would be the best way to have it so the "return mutedUser" would keep the "muted" string addition based on the conditions of the if statement being met? Thanks!
*The console.log's were me checking to see where the persistance stops
this.isUserMuted = function isUserMuted(payload) {
var mutedUser = '';
// If mute button is clicked place them into muted users list
// check for duplicates in list
$("#messages-wrapper").off('click', '.message button.muteButton');
$("#messages-wrapper").on('click', '.message button.muteButton', function(e) {
$('#unMute').show();
//create userId reference variable
var chatUserID = parseInt($(this).parent().parent().attr("data-type"));
//store userId in muted user object
mutedUsers[chatUserID] = {};
mutedUsers[chatUserID].id = chatUserID;
mutedUsers[chatUserID].muted = true;
if (mutedUsers[chatUserID] !== null && mutedUsers[chatUserID].id === payload.a) {
console.log("user is now muted");
mutedUser += ' muted';
console.log(mutedUser + 1);
}
console.log(mutedUser + 2);
});
return mutedUser;
};
If I understood what you're trying to do (by looking at the code), this would be the best approach:
// If mute button is clicked place them into muted users list
// check for duplicates in list
$("#messages-wrapper").off('click', '.message button.muteButton');
$("#messages-wrapper").on('click', '.message button.muteButton', function(e) {
$('#unMute').show();
//create userId reference variable
var chatUserID = parseInt($(this).parent().parent().attr("data-type"));
//store userId in muted user object
mutedUsers[chatUserID] = {};
mutedUsers[chatUserID].id = chatUserID;
mutedUsers[chatUserID].muted = true;
});
this.isUserMuted = function isUserMuted(payload) {
var mutedUser = '';
if (mutedUsers[payload.a] !== null) {
mutedUser += ' muted';
}
return mutedUser;
};
The code retains the array of mutedUsers, and isUserMuted function checks if provided user is in that array. In the code you provided, you would attach a new event handler every time isUserMuted function is called..
The isUserMuted function could even be shortened to:
this.isUserMuted = function isUserMuted(payload) {
return mutedUsers[payload.a] !== null ? ' muted' : '';
};
Edit
Sorry, my mistake. Another way is to pass in that variable, i.e.
this.isUserMuted = function isUserMuted(payload, isMuted) {
isMuted = '';
// If mute button is clicked place them into muted users list
// check for duplicates in list
$("#messages-wrapper").off('click', '.message button.muteButton');
$("#messages-wrapper").on('click', '.message button.muteButton', function(e) {
$('#unMute').show();
//create userId reference variable
var chatUserID = parseInt($(this).parent().parent().attr("data-type"));
//store userId in muted user object
mutedUsers[chatUserID] = {};
mutedUsers[chatUserID].id = chatUserID;
mutedUsers[chatUserID].muted = true;
if (mutedUsers[chatUserID] !== null && mutedUsers[chatUserID].id === payload.a) {
console.log("user is now muted");
isMuted += ' muted';
console.log(mutedUser + 1);
}
console.log(mutedUser + 2);
});
return isMuted;
};
You can't. If you return a string from the function, it will always be passed by value, i.e. copied; and it's value will not change any more. You would need to return a function that can access the current value of the local variable, or an object with a property that is changing.
As you already seem to have an object, option#2 will fit in well here:
function User() { // or whatever you have
…
var user = this;
// If mute button is clicked place them into muted users list
// check for duplicates in list
$("#messages-wrapper").on('click', '.message button.muteButton', function(e) {
$('#unMute').show();
//store userId in muted user object
mutedUsers[user.id] = user;
user.muted = true;
});
this.muted = false;
this.isUserMuted = function() {
return this.muted ? ' muted' : '';
}
}

Categories