I have a feature in my app wherein a user selects keywords and these keywords are displayed on a textfield once they are selected. If the page reloads, I want the keywords to remain and at the same time when the user selects more keywords, it will just be displayed along with the initial keywords.
I have checked out and tried this answer Append data to localStorage object but I get "keylocal.push is not a function" error.
if(localStorage.getItem('keywords') == null){
$('.words').on('click', function(e){
e.preventDefault();
keywords.push( $(this).data('word-name') );
document.getElementById('display').value = keywords
localStorage.setItem('keywords', keywords)
return keywords
console.log(keywords)
});
}
else{
var keylocal = localStorage.getItem('keywords')
document.getElementById('display').value = keylocal
$('.words').on('click', function(e){
keylocal.push( $(this).data('word-name') );
localStorage.setItem('keywords', keylocal)
var keyresult = localStorage.getItem('keywords')
console.log(keyresult)
document.getElementById('display').value = localStorage.getItem('keyresult')
return keywords
});
}
I expect to be able to display the previous keywords selected and the new keywords through onclick. However, it either only displays the new keywords or I get the .push is not a function error
try setting items on local storage using JSON.stringify and JSON.parse before pushing.
JSON.parse and JSON.stringify because Local storage only allows you to store string only.
sample working code
if(localStorage.getItem('keywords') == null){
$('.words').on('click', function(e){
e.preventDefault();
keywords.push( $(this).data('word-name') );
document.getElementById('display').value = keywords
localStorage.setItem('keywords', JSON.stringify(keywords)) // <- here
return keywords
console.log(keywords)
});
}
else{
var keylocal = JSON.parse(localStorage.getItem('keywords')) // <- here
document.getElementById('display').value = keylocal
$('.words').on('click', function(e){
keylocal.push( $(this).data('word-name') );
localStorage.setItem('keywords', JSON.stringify(keylocal)) // <- here
var keyresult = JSON.parse(localStorage.getItem('keywords')) // <- here
console.log(keyresult)
document.getElementById('display').value = JSON.parse(localStorage.getItem('keyresult'))
return keywords
});
}
First you need to get data from localStorage while onClick and append localStorage data with your data.
Related
I want to be able to write something inside off my text box and then get the value off it added to my objects that i have pushed into a list "let myToDos = [];"
window.onload = function(){
// without this my site keeps realoding when adding a new item
let firstTask = new Todo ('Bädda sängen');
let secondTask = new Todo ('Hänga upp tavlorna');
let thirdTask = new Todo ('Kick back & realx');
// Adding my premade todo's into my Array that has the variable 'myToDos'
myToDos.push(firstTask);
myToDos.push(secondTask);
myToDos.push(thirdTask);
// creating a function so that the user can add a new todo
let addButton = document.getElementById('addBtn');
addButton.addEventListener('click',addNewTask);
preMadeTasks ();
console.log(myToDos);
}
let myToDos = [];
class Todo{
constructor(toDoItem){
this.toDoItem = toDoItem;
}
}
function addNewTask (e){
e.preventDefault();
let test = document.getElementById ("mySection");
let inputValue = document.getElementById('textBox').value;
if (inputValue == ""){
alert("Type in something");
}[enter image description here][1]
else{
myToDos.push(inputValue);
test.innerHTML="";
preMadeTasks();
}
}
In the addNewTask function you are pushing a string value into the myToDos array instead of an instance of the Todo class. You have to do:
myToDos.push(new Todo(inputValue));
Im trying to display all the records that are in local storage.
Ive currently only managed to temporarily show records upon creation however they dissapear once refreshing the page.
let tunings = [];
// example {id:1592304983049, title: 'Deadpool', year: 2015}
const addTuning = (ev) => {
ev.preventDefault(); //to stop the form submitting
let tuning = {
name: document.getElementById('name').value,
note1: document.getElementById('note1').value,
note2: document.getElementById('note2').value,
note3: document.getElementById('note3').value,
note4: document.getElementById('note4').value,
note5: document.getElementById('note5').value,
note6: document.getElementById('note6').value
}
tunings.push(tuning);
document.forms[0].reset();
// to clear the form for the next entries
//document.querySelector('form').reset();
//display data
console.warn('added', {
tunings
});
let pre = document.querySelector('#msg pre');
pre.textContent = '\n' + JSON.stringify(tunings, '\t', 2);
//saving to localStorage
localStorage.setItem('MyTuningList', JSON.stringify(tunings));
}
document.addEventListener('DOMContentLoaded', () => {
document.getElementById('btn').addEventListener('click', addTuning);
});
This here displays data upon the creation of records however id like to grab every record in local storage and display it on the html page.
//display data
console.warn('added', { tunings });
let pre = document.querySelector('#msg pre');
pre.textContent = '\n' + JSON.stringify(tunings, '\t', 2);
You'll need to parse the data to get it in correct format.
This example relies on having the existence of a storage item called tunings
const data = localStorage.getItem("tunings"); // Store the localstorage data in variable.
// Set it to an empty array incase the storage is empty.
if (!tunings || !tunings.length) {
tunings = [];
} else {
tunings = JSON.parse(data); // Parse the data.
}
console.log(tunings); // Read the data for example.
I am being asked to have a to do list and save each task (that the user supplies as well as original) through local storage. My teacher did a very simple demo on something completely different and I spent a few hours trying to figure it out. When I looked at the solution, I honestly cannot figure it out. It looks really complicated, and I don't even know where to start. If anyone can give me any hints, that would be awesome!
My code:
let ul = document.querySelector('ul');
let newItem = document.querySelector('input[type=text]');
let checkbox = document.createElement('input');
checkbox.setAttribute('type', 'checkbox');
function output() {
let newTodo = document.createElement('li');
newTodo.innerText = newItem.value;
newTodo.classList.add('todo');
let ulAppend = ul.append(newTodo);
ul.append(newTodo);
let checkboxAppend = newTodo.append(checkbox);
newTodo.append(checkbox);
newItem.value = '';
}
let button = document.querySelector('.btn');
button.addEventListener('click', output);
ul.addEventListener('click', function(e) {
if (e.target.tagName === 'LI') {
e.target.remove();
} else if (e.target.tagName === 'INPUT') {
e.target.parentElement.classList.toggle('finished');
}
});
My teacher's code/local storage solution:
const todoForm = document.getElementById("newTodoForm");
const todoList = document.getElementById("todoList");
// retrieve from localStorage
const savedTodos = JSON.parse(localStorage.getItem("todos")) || [];
for (let i = 0; i < savedTodos.length; i++) {
let newTodo = document.createElement("li");
newTodo.innerText = savedTodos[i].task;
newTodo.isCompleted = savedTodos[i].isCompleted ? true : false;
if (newTodo.isCompleted) {
newTodo.style.textDecoration = "line-through";
}
todoList.appendChild(newTodo);
}
todoForm.addEventListener("submit", function(event) {
event.preventDefault();
let newTodo = document.createElement("li");
let taskValue = document.getElementById("task").value;
newTodo.innerText = taskValue;
newTodo.isCompleted = false;
todoForm.reset();
todoList.appendChild(newTodo);
// save to localStorage
savedTodos.push({ task: newTodo.innerText, isCompleted: false });
localStorage.setItem("todos", JSON.stringify(savedTodos));
});
todoList.addEventListener("click", function(event) {
let clickedListItem = event.target;
if (!clickedListItem.isCompleted) {
clickedListItem.style.textDecoration = "line-through";
clickedListItem.isCompleted = true;
} else {
clickedListItem.style.textDecoration = "none";
clickedListItem.isCompleted = false;
}
// breaks for duplicates - another option is to have dynamic IDs
for (let i = 0; i < savedTodos.length; i++) {
if (savedTodos[i].task === clickedListItem.innerText) {
savedTodos[i].isCompleted = clickedListItem.isCompleted;
localStorage.setItem("todos", JSON.stringify(savedTodos));
}
}
});
Even though my code is more simpler (at least from what I can tell), it works exactly as his code does.
Local storage saves a JSON object to the user's computer. You should create an array of todos, append that array with every new todo, then set that item to local storage.
let ul = document.querySelector('ul');
const savedTodos = JSON.parse(localStorage.getItem("todos")) || []; // Retrieves local storage todo OR creates empty array if none exist
let newItem = document.querySelector('input[type=text]');
let checkbox = document.createElement('input');
checkbox.setAttribute('type', 'checkbox');
function output() {
let newTodo = document.createElement('li');
newTodo.innerText = newItem.value;
newTodo.classList.add('todo');
ul.append(newTodo);
newTodo.append(checkbox);
savedTodos.push({task: newItem.value, isCompleted: false}); // Appends the new todo to array
localStorage.setItem("todos", JSON.stringify(savedTodos)); //Converts object to string and stores in local storage
newItem.value = '';
}
I've annotated the solution you posted with some comments to help you step through it.
// Retrieve elements and store them in variables
const todoForm = document.getElementById("newTodoForm");
const todoList = document.getElementById("todoList");
// Get data stored in localStorage under the key "todos".
// The data type will be a string (local storage can only store strings).
// JSON is a global object that contains methods for working with data represented as strings.
// The `||` syntax is an OR operator and is used here to set an empty array as a fallback in case `localStorage` is empty
const savedTodos = JSON.parse(localStorage.getItem("todos")) || [];
// Create a loop the same length as the list of todos
for (let i = 0; i < savedTodos.length; i++) {
// Create an <li> element in memory (does not appear in the document yet)
let newTodo = document.createElement("li");
// Set the inner text of that new li with the contents from local storage.
// The savedTodos[i] is accessing data in the localStorage array.
// The [i] is a different number each loop.
// The `.task` is accessing 'task' property on the object in the array.
newTodo.innerText = savedTodos[i].task;
// Create a new property on the element called `isCompleted` and assign a boolean value.
// This is only accessible in code and will not show up when appending to the DOM.
newTodo.isCompleted = savedTodos[i].isCompleted ? true : false;
// Check the value we just set.
if (newTodo.isCompleted) {
// Create a style for the element if it is done (strike it out)
newTodo.style.textDecoration = "line-through";
}
// Actually append the new element to the document (this will make it visible)
todoList.appendChild(newTodo);
}
// `addEventListener` is a function that registers some actions to take when an event occurs.
// The following tells the browser - whenever a form is submitted, run this function.
todoForm.addEventListener("submit", function(event) {
// Don't try to send the form data to a server. Stops page reloading.
event.preventDefault();
// Create a <li> element in memory (not yet visible in the document)
let newTodo = document.createElement("li");
// Find element in the document (probably a input element?) and access the text value.
let taskValue = document.getElementById("task").value;
// Set the text of the <li>
newTodo.innerText = taskValue;
// Set a property on the <li> call `isCompleted`
newTodo.isCompleted = false;
// Empty out all the input fields in the form
todoForm.reset();
// Make the new <li> visible in the document by attaching it to the list
todoList.appendChild(newTodo);
// `push` adds a new element to the `savedTodos` array. In this case, an object with 2 properties.
savedTodos.push({ task: newTodo.innerText, isCompleted: false });
// Overwrite the `todos` key in local storage with the updated array.
// Use the JSON global object to turn an array into a string version of the data
// eg [1,2,3] becomes "[1,2,3]"
localStorage.setItem("todos", JSON.stringify(savedTodos));
});
// This tells the browser - whenever the todoList is clicked, run this function.
// The browser will call the your function with an object that has data about the event.
todoList.addEventListener("click", function(event) {
// the `target` of the event is the element that was clicked.
let clickedListItem = event.target;
// If that element has a property called `isCompleted` set to true
if (!clickedListItem.isCompleted) {
// update the styles and toggle the `isCompleted` property.
clickedListItem.style.textDecoration = "line-through";
clickedListItem.isCompleted = true;
} else {
clickedListItem.style.textDecoration = "none";
clickedListItem.isCompleted = false;
}
// The code above changes the documents version of the data (the elements themselves)
// This loop ensures that the array of todos data is kept in sync with the document
// Loop over the array
for (let i = 0; i < savedTodos.length; i++) {
// if the item in the array has the same text as the item just clicked...
if (savedTodos[i].task === clickedListItem.innerText) {
// toggle the completed state
savedTodos[i].isCompleted = clickedListItem.isCompleted;
// Update the localStorage with the new todos array.
localStorage.setItem("todos", JSON.stringify(savedTodos));
}
}
});
Keep in mind, there are 2 sources of state in your todo list. One is how the document looks, and the other is the array of todos data. Lots of challenges come from making sure these 2 stay in sync.
If somehow the document showed one of the list items as crossed out, but your array of data shows that all the todos are not completed, which version is correct? There is no right answer here, but state management will be something you might consider when designing apps in the future. Redux is a good js library with a well understood pattern that helps solve this problem. Hope this last comment doesn't confuse too much. Best of luck!
The important part is in (de)serializing the data. That means:
reading from localStorage (JSON.parse(localStorage.getItem("todos")) || [])
We add the default [] because if the todos key does not exist, we will get null and we expect a list
saving to localStorage (localStorage.setItem("todos", JSON.stringify(savedTodos)))
We need JSON.parse and its complementary operation JSON.stringify to parse and save strings because localStorage can store only strings.
In your case you need to read the data from localStorage and render the initial list. To save it to localStorage, again, you have to serialize the data. See the below snippets (link to working JSFIDDLE, because the below example does not work in the StackOverflow sandbox environment):
let ul = document.querySelector('ul');
let newItem = document.querySelector('input[type=text]');
const Store = {
serialize () {
return [].slice.call(document.querySelectorAll("li")).map(c => {
return {
text: c.textContent,
finished: c.querySelector("input").checked
}
})
},
get () {
return JSON.parse(localStorage.getItem("todos")) || []
},
save () {
return localStorage.setItem("todos", JSON.stringify(Store.serialize()))
}
}
const firstItems = Store.get()
firstItems.forEach(it => {
output(it.text, it.finished)
})
function output(v, finished) {
let newTodo = document.createElement('li');
newTodo.innerText = v || newItem.value;
newTodo.classList.add('todo');
let ulAppend = ul.append(newTodo);
ul.append(newTodo);
// Create a checkbox for each item
let checkbox = document.createElement('input');
if (finished) {
checkbox.checked = true
}
checkbox.setAttribute('type', 'checkbox');
let checkboxAppend = newTodo.append(checkbox);
newTodo.append(checkbox);
newItem.value = '';
}
let button = document.querySelector('.btn');
button.addEventListener('click', () => {
output()
Store.save()
});
ul.addEventListener('click', function(e) {
if (e.target.tagName === 'LI') {
e.target.remove();
} else if (e.target.tagName === 'INPUT') {
e.target.parentElement.classList.toggle('finished');
}
// Update the value in localStorage when you delete or add a new item
Store.save()
});
<ul></ul>
<input type="text" /> <button class="btn">Submit</button>
I have added the Store variable to simplify the way you get and set the data in localStorage.
The serialize method will read the TODOs from the list. document.querySelectorAll("li") returns a NodeList, but by doing [].slice.call(...) we convert it to an Array.
I am trying to edit/update current data using the contenteditable attribute which I have successfully enabled onclick. My 'enter' key allows the data to be submitted. However, the console.log reads that a PUT request has been made for a particular list item but without the 'title' or 'isbn' being updated along with it.
Another prominent issue is that my console.log shows books.forEach is not a function, and I have no idea why this is the case since the code inside that function is processed.
HTML ('li' items are solely JS-Generated with a POST request)
<div id="divShowBooks">
<li id="[object HTMLParagraphElement]">
<p id="24" name="anID" placeholder="24">1</p>
<p id="TEST" name="aTitle" placeholder="TEST">TEST</p>
<p id="12345" name="anISBN" placeholder="12345" contenteditable="true">12345</p>
<button>Delete</button>
</li>
</div>
JavaScript
var book_list = document.querySelector('#divShowBooks');
book_list.innerHTML = "";
var books = JSON.parse(this.response);
books.forEach(function (book) {
// Text information to be displayed per item
var id = document.createElement('p');
id.type = 'text';
id.innerHTML = book.id;
var title = document.createElement('p');
title.type = 'text';
title.innerHTML = book.title;
var isbn = document.createElement('p');
isbn.type = 'text';
isbn.innerHTML = book.isbn;
// Defining the element that will be created as a list item
var book_item = document.createElement('li');
// Displays id, title and ISBN of the books from the database
book_item.appendChild(id);
book_item.appendChild(title);
book_item.appendChild(isbn);
// Creates an ID attribute per list item
book_item.setAttribute("id", id)
// Assigns attributes to p items within book items
id.setAttribute("id", book.id)
title.setAttribute("id", book.title)
isbn.setAttribute("id", book.isbn)
// Adding a generic name to these elements
id.setAttribute("name", "anID")
title.setAttribute("name", "aTitle")
isbn.setAttribute("name", "anISBN")
title.addEventListener('click', function (e) {
e.preventDefault();
title.contentEditable = "true";
title.setAttribute("contenteditable", true);
title.addEventListener('keypress', function (e) {
if (e.keyCode === 13) {
e.preventDefault();
xhttp.open("PUT", books_url + '/' + book.id, true);
var editTitle = new FormData() /
editTitle.append("title", document.getElementsByName("aTitle")[0].value)
xhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded')
xhttp.send(); //
}
});
});
UPDATE
I have added the following to my code. This seems to display my database items as an array in the log. But, I am now having a similar issue with Uncaught TypeError: JSON.parse(...).map is not a function:
var params = [
id = 'id',
title = 'title',
isbn = 'isbn',
createdAt = 'createdAt',
updatedAt = 'updatedAt'
];
var books = JSON.parse(this.response).map(function(obj) {
return params.map(function(key) {
return obj[key];
});
});
console.log(books);
UPDATE 2
Here is an image of what I receive in the console.log. The first part displays the original JSON content and the second is my attempt to convert each object into an array.
See Image
You have to make sure that your books variable actually contains an Array after parsing.
Alternatively, but this wouldn't make sense, just to address the "books.forEach is not a function" issue, You can use Object.assign([], this.response);. To make sure that books will contain an array, you wrap it in a try catch and make something like this:
var books = [];
try {
books = Object.assign([], this.response);
} catch (error) {
books = [];
}
books.forEach will then be expected to always work but you have to be careful because something like this could happen:
var myStringObject = "{'myProperty':'value'}";
var myArray = Object.assign([], myStringObject );
//myArray value is ["{", "'", "myProperty", "'", ":", "'", "value", "'", "}"]
Which will leave you having to check the book in your forEach callback if it is correct:
//at the topmost of your forEach callback
if(!book.id) throw BreakException; //A simple break will not work on forEach
This will leave you again with another exception to handle. Or leave you having to use the traditional for loop since you cannot short circuit Array.forEach with a break.
TLDR: make sure books always contains an Array.
You are getting books from JSON.parse(), which means books is an object and not an array.
forEach is an array method.
Try console logging books and look for an array inside of it.
I am using a simple code to hide multiple divs if the link to hide them is clicked.
On one of them I have local storage set-up to remember of the div is hidden or not.
The thing is this. How can I write a code to make local storage remember the hidden state of multiple divs WITHOUT having to put localstorage.setItem for each individual div. Is it possible to store an array with the div ids and their display set to true or false to decide if the page should show them or not?
**********EDITED************
function ShowHide(id) {
if(document.getElementById(id).style.display = '') {
document.getElementById(id).style.display = 'none';
}
else if (document.getElementById(id).style.display = 'none') {
document.getElementById(id).style.display = '';
}
Like you said, you already have an array storing all your state. You can serialize this using JSON.stringify() and put the result into localStorage.
// your array
var divstate = [ ... ];
// store it
localStorage.setItem( 'divstate', JSON.stringify( divstate ) );
If you want to retrieve the array again, use JSON.parse():
// restore
var divstate = JSON.parse( localStorage.getItem( 'divstate' );
EDIT
To store the actual ids of all divs, you probably will use something like this
var divstate = {
'divid1': true,
'divid2': false,
...
};
This can easily be used with the above pattern.
2nd EDIT
For the above code I would suggest loading the state variable once at page load using the above statement.
Then transform the function like this:
function ShowHide(id) {
var el = document.getElementById(id);
if ( divstate[id] === true ) {
divstate[id] = false;
el.style.display = 'none';
} else {
divstate[id] = true;
el.style.display = '';
}
localStorage.setItem( 'divstate', JSON.stringify( divstate ) );
}
That way the divstate is updated and stored with each function call.
Note, that I would not recommend this, if the number of divs is too high, but for smaller amounts this should be sufficient.