I am trying to store two type of lists (add, remove) into localStorage. By default, I will add some values. And then user can remove that name if they don't want it. Once they remove the name from #notneeded list it will add into #needed list. I have done that case too.
JsBin Link
Default Case:
Problem is Here:
Now, you can see. I clicked EEE from second list. In visual screen it works fine. EEE removed from second list and added into first list. In localStorage first list is working fine as expected.
add => ["AAA","BBB","EEE"]
But, In second list I am expecting it should update like this
remove => ["CCC","DDD","FFF","GGG"]
instead of this
["CCC","DDD","EEE","FFF","GGG","\n\t\tCCCDDDFFFGGG"]
What I am doing wrong here?
Javascript
const selected = document.getElementById('needed');
const unselect = document.getElementById('notneeded');
//selected lists onload event
window.addEventListener('load', function(e){
var getLists = localStorage.getItem("add");
if (getLists === null) {
const defaultLists = [ 'AAA', 'BBB' ];
const unselectedLists = [ 'CCC', 'DDD', 'EEE', 'FFF', 'GGG' ];
defaultLists.forEach(item => {
liMaker(0, item);
});
unselectedLists.forEach(item => {
liMaker(1, item);
});
localStorage.setItem('add', JSON.stringify(defaultLists));
localStorage.setItem('remove', JSON.stringify(unselectedLists));
} else {
const defaultLists = JSON.parse(localStorage.getItem('add'));
const unselectedLists = JSON.parse(localStorage.getItem('remove'));
defaultLists.forEach(item => {
liMaker(0, item);
});
unselectedLists.forEach(item => {
liMaker(1, item);
});
}
});
//unselected lists onclick event
unselect.addEventListener('click', function(e){
const tgt = e.target;
let liArray = JSON.parse(localStorage.getItem('add')) || [];
liMaker(0, tgt.innerHTML);
liArray.push(tgt.innerHTML);
localStorage.setItem('add', JSON.stringify(liArray));
if(tgt.tagName.toUpperCase() == "LI") {
e.target.parentNode.removeChild(tgt);
}
const unselect = document.getElementById('notneeded').innerHTML;
let uliArray = JSON.parse(localStorage.getItem('remove')) || [];
uliArray.push(unselect);
console.log(uliArray);
localStorage.setItem('remove', JSON.stringify(uliArray));
});
const liMaker = (num, text) => {
const li = document.createElement('li');
li.textContent = text;
if(num === 0) {
selected.appendChild(li);
} else if(num === 1) {
unselect.appendChild(li);
}
}
HTML
<ul id="needed">
</ul>
<ul id="notneeded">
</ul>
Problem is here
const unselect = document.getElementById('notneeded').innerHTML;
This returns string, not actual values you expect
const unselect = document.getElementById('notneeded')
const badValues = unselect.innerHTML
const goodValues = [].map.call(unselect.children, (e) => e.textContent) // using .call
const goodValues2 = [...unselect.children].map(e => e.textContent) // using spread operator
console.log(badValues)
console.log(goodValues)
console.log(goodValues2)
<ul id='notneeded'>
<li>one</li>
<li>two</li>
</ul>
You are appending the innerHtml of the ul to the uliArray
const unselect = document.getElementById('notneeded').innerHTML;
let uliArray = JSON.parse(localStorage.getItem('remove')) || [];
uliArray.push(unselect);
Related
How do I implement/execute where once I click the edit button it will allow the user to input a value then once submitted, the text in the li will render the updated value?
JS code block is written below:
P.S. You can ignore the other functions that are irrelevant.
P.S. I know, the edittask is incomplete but I'm not exactly sure how to implement the functionality I mentioned above.
const alertMsg = document.querySelector('.alert-message');
const inputForm = document.querySelector('.input-section');
const todoInput = document.querySelector('.todo-input');
const addBtn = document.querySelector('.add-btn');
const taskActions = document.querySelector('.task-actions');
const todosList = document.querySelector('.todos-list');
const deleteAllBtn = document.querySelector('.delete-all-btn');
const savedTodos = localStorage.getItem('todos');
let todos = [];
function displayTodos(newTodoObj){
const li = document.createElement('li');
li.id = newTodoObj.id;
li.className = 'task-container'
const task = document.createElement('span');
const checkBtn = document.createElement('button')
const editBtn = document.createElement('button')
const deleteBtn = document.createElement('button')
task.innerText = newTodoObj.text;
checkBtn.innerText = 'Check'
editBtn.innerText = 'Edit';
deleteBtn.innerText = 'Del';
checkBtn.addEventListener('click', (event) => {
const task = event.target.parentElement;
console.log(task);
task.classList.toggle('completed');
})
editBtn.addEventListener('click', editTask)
deleteBtn.addEventListener('click', deleteTask)
li.appendChild(task);
li.appendChild(checkBtn);
li.appendChild(editBtn);
li.appendChild(deleteBtn);
todosList.appendChild(li);
}
function editTask(event){
const li = event.target.parentElement.children[0].innerText;
todoInput.value = li;
}
function deleteTask(event){
const li = event.target.parentElement;
li.remove();
todos = todos.filter((todo) => todo.id !== parseInt(li.id));
saveTodos();
}
function handleTodoSubmit(event){
event.preventDefault();
const newTodo = todoInput.value;
todoInput.value = '';
const newTodoObj = {
text: newTodo,
id: Date.now(),
checked: false
};
todos.push(newTodoObj);
displayTodos(newTodoObj);
saveTodos();
}
function saveTodos(){
localStorage.setItem('todos', JSON.stringify(todos));
}
inputForm.addEventListener('submit', handleTodoSubmit);
if(savedTodos !== null){
const parsedTodos = JSON.parse(savedTodos);
parsedTodos.forEach(displayTodos);
}
window.addEventListener('beforeunload', saveTodos);
This code adds an input element to the DOM when the "Edit" button is clicked, sets its value to the text of the task, and adds an event listener that listens for the "Enter" key. When the "Enter" key is pressed, the code updates the text of the task and replaces the input element with a span element containing the updated text. It also updates the todos array and saves the updated array to local storage.
function editTask(event){
const li = event.target.parentElement;
const task = li.children[0];
const input = document.createElement('input');
input.value = task.innerText;
li.replaceChild(input, task);
input.focus();
input.addEventListener('keydown', (event) => {
if (event.key === 'Enter') {
const newTask = document.createElement('span');
newTask.innerText = input.value;
li.replaceChild(newTask, input);
const todoIndex = todos.findIndex((todo) => todo.id === parseInt(li.id));
todos[todoIndex].text = newTask.innerText;
saveTodos();
}
});
}
You can use this code in your existing JavaScript file by replacing the current editTask function with this one.
I don't know if I understood your question very well, but I hope it will at least help guide you. Or maybe it is the complete solution. Best wishes!
I'm working on a project where I need to filter 13 items by two different select box values, and I'm getting stuck on persisting the filter.
I have two select boxes that I've selected like so:
let pickupLocation = document.querySelector("#pa_location"); //values are 'complete-set', 'neck', 'bridge'.
let pickupType = document.querySelector("#pa_type1"); // Values are 'soapbar', 'dogear', 'short'.
What's Working:
I'm initializing an object like so:
const activeFilters = {};
To populate the values like so:
//Persist the Complete Set / Single
pickupLocation.addEventListener("change", function () {
if (pickupLocation.value === "complete-set") {
activeFilters.location = "set";
} else {
activeFilters.location = "single";
}
});
pickupType.addEventListener("change", function () {
if (pickupType.value === "soapbar") {
activeFilters.type = "soapbar";
} else if (pickupType.value === "dogear") {
activeFilters.type = "dogear";
} else {
activeFilters.type = "short";
}
});
// Returns something like
// {location: single, type: dogear}
I'm trying to filter an array of input elements by their value. I have 13 inputs each with a value containing words like set, single, dogear, soapbar etc.
Where I'm stuck:
I have a filter function that I'm trying to filter the values of these inputs by two values of the activeFilters object:
const performFilter = (covers) => {
let results;
let filteredValues = Object.values(activeFilters);
filteredValues.forEach((value) => {
results = covers.filter((cover) => cover.value.indexOf(value) !== -1);
});
return results;
};
The problem is my function is returning only one of the two words. For instance, if the my activeFilters object is {location: set, type: dogear} the filtered results array contains only one of them. Where am I going wrong?
Edit:
This function returns all inputs that match one of the activeFilters, and I apologize if I wasn't clear above, but I'd like it to match ALL of the Active Filters. Is this possible with the function below?
const performFilter = (covers) => {
let results = []; // initialise the array
let filteredValues = Object.values(activeFilters);
filteredValues.forEach((value) => {
let res = covers.filter((cover) => cover.value.indexOf(value) !== -1);
results.push(...res);
});
console.log(results);
};
CODEPEN:
Codepen!
const performFilter = (covers) => {
let results = []; // initialise the array
let filteredValues = Object.values(activeFilters);
filteredValues.forEach((value) => {
let res = covers.filter((cover) => cover.value.indexOf(value) !== -1);
// push the value it find individually
// you were overriding the previous value with result = filter()
results.push(...res);
});
return results;
};
// according to Edited question
const performFilter = (covers) => {
let results = []; // initialise the array
let filteredValues = Object.values(activeFilters);
return covers.filter((cover) => filteredValues.every(value => cover.value.indexOf(value) !== -1));
};
I'm not sure if I understood clearly your question, so feel free to comment it.
First, I suggest you to filter your covers array and inside the filtering function iterate through your selected filters. This is because the filter function returns the array already filtered and so you don't need to assign it to a result variable or things like that. So based on that, try this:
const performFilter = (covers) => {
let results;
let filteredValues = Object.values(activeFilters);
const filteredCovers = covers.filter((cover) => {
return cover.value.split("-").some((tag) => filteredValues.includes(tag))
});
console.log(filteredCovers)
};
I've created a to-do list with local storage. If you create three list items and delete the second one, the deleted list item will reappear in place of the third item on refresh.
Edit: I'm not sure whether it's to do with local storage or with the original todo array. In the code below, I'm trying to remove the relevant value from the array, but I suspect this isn't working (logging the array to the console produces no result).
Although it has nothing to do with local storage, I think the issue lies with the following code:
function removeItem() {
let item = this.parentNode.parentNode;
let parent = item.parentNode;
let id = parent.id;
console.log(id)
let value = parent.textContent;
todo.splice(todo.indexOf(value, 1));
this.parentNode.parentNode.removeChild(this.parentNode);
saveTodos();
}
Edit: Here is the code I used to store the list items:
function saveTodos() {
let jsonstr = JSON.stringify(todo);
localStorage.setItem('todo', jsonstr);
}
function getTodos() {
localStorage.getItem('todo')
let jsonstr = localStorage.getItem("todo");
todo = JSON.parse(jsonstr);
if (!todo || !todo.length) {
todo = [];
}
else {
renderTodoList();
}
}
Here is a link to the codepen: https://codepen.io/david-webb/pen/yLeqydK
Can you help?
This is because the current code seems to be removing the wrong item.
See scenario:
Localstorage: ["t","1", "2"];
-> Remove item #2 ("t")
Localstorage: ["t", "1"];
Output:
As you can see, the output shows ["t", "2"] thought the localstorage array is ["t", "1"].
This is because of the flawed logic in the removeItem function.
Try with this, instead.
//remove list item on click
function removeItem() {
const item = this.parentNode;
const value = this.parentNode.lastChild.textContent;
todo = todo.filter(t => t !== value);
this.parentNode.parentNode.removeChild(item);
saveTodos();
}
fiddle:
<input type = "text" style="font-size:25px;" id = "input" placeholder="Write here">
<button id = "addBtn" >Add item</button>
<ul id = "myUL">
</ul>
<script>
let todo = [];
renderTodoList();
document.getElementById('addBtn').addEventListener('click', function () {
let value = document.getElementById('input').value;
if (value) {
todo.push(value);
saveTodos()
addInput(value);
}
});
input.addEventListener("keypress", function(event) {
// Number 13 is the "Enter" key on the keyboard
if (event.keyCode === 13) {
// Trigger the button element with a click
document.getElementById("addBtn").click();
}
});
function addInput(text) {
//add list item on click
let listItem = document.createElement('li');
let list = document.getElementById('myUL');
let input = document.getElementById('input').value;
let textNode = document.createTextNode(text);
//create and append remove button
let removeBtn = document.createElement("BUTTON");
list.appendChild(removeBtn);
removeBtn.className = "removeBtn";
removeBtn.innerHTML = "Remove item";
listItem.appendChild(removeBtn);
list.appendChild(listItem);
listItem.appendChild(textNode);
document.getElementById("input").value = "";
removeBtn.addEventListener('click', removeItem);
console.log(todo);
}
//remove list item on click
function removeItem() {
const item = this.parentNode;
const value = this.parentNode.lastChild.textContent;
todo = todo.filter(t => t !== value);
this.parentNode.parentNode.removeChild(item);
saveTodos();
}
function renderTodoList() {
if (!todo) return
for (let i = 0; i < todo.length; i++) {
let value = todo[i];
addInput(value);
console.log(value);
}
}
function saveTodos() {
let jsonstr = JSON.stringify(todo);
localStorage.setItem('todo', jsonstr);
}
function getTodos() {
localStorage.getItem('todo')
let jsonstr = localStorage.getItem("todo");
todo = JSON.parse(jsonstr);
if (!todo || !todo.length) {
todo = [];
}
else {
renderTodoList();
}
}
//cross out text on click
/*document.addEventListener('click', function (ev) {
if (ev.target.tagName === 'LI') {
ev.target.classList.toggle('checked');
}
});*/
//renderTodoList();
getTodos();
</script>
I think the problem is the usage of splice and indexOf.
For splice -- pass index, how may delete, new item
var todo = ["a", "b", "c"];
var value = "b"
// your code
todo.splice(todo.indexOf(value, 1));
console.log(todo)
var todo = ["a", "b", "c"];
var value = "b"
// correct way to delete
todo.splice(todo.indexOf(value), 1);
console.log(todo)
This line has the error todo.splice(todo.indexOf(value, 1));
The reason is when you apply let item = this.parentNode.parentNode; you the UL element in the variable.
Fix:
While adding the item in addInput() create a span and put the text inside the span rather than creating textNode.
when removing from todo then you should use the innerText inside SPAN tag
todo.splice(todo.indexOf(value, 1));
In the value variable you should have the todo item name.
I am new to JavaScript. I have a small code that creates list from input and then adds it to an array. I am able to remove one item from the DOM when the item is clicked, but I couldn't remove it from the array.
I tried to use array.splice(item, 1)
lists.addEventListener("click", function (e) {
e.target.closest("li").remove();
userInputArr.splice(item, 1);});
But it removes the entire array sometime, and sometime removes the last item. when I console log the code, it looks like I clicked 3 or 4 times on the list even though I just clicked once. I have no idea what's wrong. this is the entire code:
const lists = document.querySelector(".lists");
const userInput = document.querySelector(".add-note");
const addBtn = document.querySelector(".add-btn");
const item = document.querySelectorAll(".list");
userInputArr = [];
function addNote() {
if (userInput.value < 1) {
return;
}
lists.insertAdjacentHTML(
"afterbegin",
`<li class='list'>${userInput.value}</li>`
);
userInputArr.push(lists);
lists.addEventListener("click", function (e) {
e.target.closest("li").remove();
userInputArr.splice(item, 1);
});
userInput.value = "";
}
addBtn.addEventListener("click", function () {
addNote();
});
Code is totally meaningless
1)
userInputArr.push(lists)
why you push the same element all the time? As lists refers to the first and the only element with class 'lists'?
2)
userInputArr.splice(item, 1)
please watch carefully what splice does? The first argument is number, but you pass a collection of elements with class 'list'. But i camn not even suggest which element should be removed as it contains the same element as i mentioned in first point
3) You do not need this array at all
So right approach is something like this
const lists = document.querySelector(".lists");
// just once create listener, no need to do it each time
lists.addEventListener("click", function (e) {
// if you want to remove clicked item then
if (e.target.tagName === 'LI') e.target.remove();
// but if you want to remove the first one then uncomment line
// if (this.children[0]) this.children[0].remove()
});
const userInput = document.querySelector(".add-note");
const addBtn = document.querySelector(".add-btn");
///////////////////////////////////////////////////
// item is meaninglee here, so delete this line
// const item = document.querySelectorAll(".list");
//////////////////////
// array is useless too, delete this line
// userInputArr = [];
function addNote() {
// check if it is number
if (isNaN(userInput.value) || Number(userInput.value < 1)) {
return;
}
lists.insertAdjacentHTML(
"afterbegin",
`<li class='list'>${userInput.value}</li>`
);
userInput.value = "";
}
addBtn.addEventListener("click", function () {
addNote();
});
const items = (() => {
const _items = {};
let key = 0;
return {
put(value) {
_items[key++] = value;
console.log("Added", this.all());
return key - 1;
},
remove(key) {
delete _items[key++];
console.log("Removed", this.all());
},
all(asArray = true) {
return asArray ? Object.values(_items) : { ..._items
};
}
}
})();
const inputEl = document.querySelector(".input");
const itemsEl = document.querySelector(".items");
const addBtn = document.querySelector(".btn-add");
addBtn.addEventListener("click", () => {
const value = inputEl.value.trim();
if (!value.length) return;
const key = items.put(value);
const li = document.createElement("li");
li.textContent = value;
li.dataset.key = key;
itemsEl.append(li);
inputEl.value = "";
});
itemsEl.addEventListener("click", (e) => {
const li = e.target.closest("li");
items.remove(li.dataset.key);
li.remove();
});
<input type="text" class="input">
<button class="btn-add">Add</button>
<ul class="items"></ul>
Run code & View in full screen.
use shift() userInputArr.shift()
you are also getting double clicks because your addNote() function contains an event listener lists.addEventListener and it's executed by another event listner addBtn.addEventListener you should probably move
lists.addEventListener out of the addNote function
I am wondering how can I add data content inside array to innerHTML using the forEach?
For some reason, It is not showing up. Is this possible to do? I am working with dynamic data this way I need to loop through my data and show them with innerHTML.
const showPosts = (data, value) => {
const btnDiscover = document.querySelector('[data-show="discover"]');
const slides = data.add_post_slides;
const Posts = () => {
slides.length > 0 &&
slides
.forEach((el) => {
return <h2>123</h2>;
})
.join('');
};
// console.log(Posts());
btnDiscover.addEventListener('click', (e) => {
e.preventDefault();
body.classList.add('toggle-legend', 'no-scroll');
console.log('Discover clicked');
console.log(slides);
theContent = `
<div class="modal">
<div class="modal__body">
<span class="modal__close"></span>
${Posts()}
</div>
</div>
`;
timelineModal.innerHTML = theContent;
onModalClose();
});
};
How can I do such a thing?
There are many way. I can suggest pick the element by using getElementById. After that create a function that will add the data in innerHtml of that element. Try to do something similar below
var fruits = ["apple", "orange", "cherry"];
fruits.forEach(myFunction);
function myFunction(item, index) {
document.getElementById("demo").innerHTML += index + ":" + item + "<br>";
}
forEach don't return responses. use Array.map or declare and initialize array variables and then push them.
const newSlides = slides.map((el) => el)
or
const elements = [];
slides.forEach((el) => elements.push(el));
// Add return. your arrow function don't return element.
const Posts = () => {
return slides.length > 0 &&
slides
.forEach((el) => {
return <h2>123</h2>;
})
.join('');
};