jQuery to do list done button not removing item - javascript

I am creating a simple to do list using jQuery and local storage. I am also trying to add a button for each li I add to clear the item from the list. My list does not stick upon refresh and I can't figure out how to load the button, does the button need to happen on the HTML side?
The adding to the list functions work great its just the storage to local storage that I seem to be missing something.
I created a jsfiddle for this code and the local storage seems to work fine but it will not work on my xampp. Also I can get the done button to appear but it won't removeItem.
https://jsfiddle.net/blen6035/287pc153/7/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Task List</title>
<link rel="stylesheet" href="main.css">
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script src="tasks.js"></script>
</head>
<body>
<aside>
<h2>Add a task</h2>
<label for="task">Task:</label>
<input type="text" id="task" name="task"><br>
<label> </label>
<input type="button" id="add" name="add" value="Add Task">
</aside>
<main>
<h1>Task list</h1>
<ul id="listOfTasks"></ul>
</main>
<footer></footer>
</body>
</html>
"use strict"
$(document).ready(function() {
let listOfTasks = JSON.parse( localStorage.getItem("tasks"));
if( listOfTasks == undefined ){
listOfTasks = [];
}
for( let i = 0; i < listOfTasks.length; i++){
let li = $('<li> Done
</li>').text(listOfTasks[i]);
$('#listOfTasks').append(li);
}
$('#add').click(function(){
let task = $('#task').val();
listOfTasks.push(task);
localStorage.setItem("tasks", JSON.stringify(listOfTasks)
);
let li = $('<li></li>').text(task);
$('#listOfTasks').append('<li>'+ task +'<input type="submit"
class="done" value= "Done">' + '</li>');
$('#task').val(' ').focus();
});
$('.done').on('click', '.delete',function(){
$(this).parent().remove();
});
/*$('#done').click(function(){
localStorage.removeItem;
$('#listOfTasks').html('');
});*/
}); // end ready

Is this what you are trying to do ?
Note that I had to polyfill local storage to make this work in a snippet, replace fakeLocalStorage by localStorage
const listOfTasksElement = $('#listOfTasks')
const taskInputElement = $('#task')
const listOfTasks = JSON.parse(fakeLocalStorage.getItem('tasks')) || []
const updateTasks = () => fakeLocalStorage.setItem('tasks', JSON.stringify(listOfTasks))
const addTask = task => {
const taskElement = $('<li></li>').text(task)
const doneElement = $('<span>Done</span>').click(() => {
const index = listOfTasksElement.find('li').index(taskElement)
taskElement.remove()
listOfTasks.splice(index, 1)
updateTasks()
})
taskElement.append(doneElement)
listOfTasksElement.append(taskElement)
listOfTasks.push(task)
updateTasks()
}
listOfTasks.forEach(addTask)
$('#add').click(() => {
addTask(taskInputElement.val())
taskInputElement.val('').focus()
})
<ul id="listOfTasks"></ul>
<input id="task"><button id="add">Add</button>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
// local storage doesn't work in stack overflow snippets,
// this is just a poor in-memory implementation
const fakeLocalStorage = {
_data: {},
setItem(k, v) { return this._data[k] = v },
getItem(k) { return this._data.hasOwnProperty(k) ? this._data[k] : null }
}
</script>

Related

Click event only works on the second click

i hope you guys fine, well..
I'm doing a To Do List, and there is a problem in my code, which I've been trying to solve for a few days, and no effective results was made..
If you guys test in the snippet with me, i am sure, that will be more
clear to understand.
When i click in some list element, my javascript should change or add the className, and add a class call 'selected'.
because, when i will click in the remove button, they will delete all elements with 'selected' classList in the list. (as you can see in the code)
But the className a not being add to the tag in the first click, just works if i click in the element one more time.
i simplified my code, just to show the real problem:
Link to jsfiddle : https://jsfiddle.net/myqrzcs2/
const textoTarefa = document.getElementById('texto-tarefa');
const criarTarefa = document.getElementById('criar-tarefa');
const listaTarefas = document.getElementById('lista-tarefas');
criarTarefa.onclick = function click() {
const lista = document.createElement('li');
lista.className = 'lista';
lista.id = 'lista';
lista.tabIndex = '0';
lista.innerHTML = textoTarefa.value;
listaTarefas.appendChild(lista);
document.body.appendChild(listaTarefas);
textoTarefa.value = '';
};
const completedLine = document.querySelector('ol');
function umClick(event) {
if (event.target.tagName === 'LI') {
const listas = document.querySelectorAll('.lista');
listas.forEach((i) => {
i.addEventListener('click', function semNomeDois() {
listas.forEach((j) => j.classList.remove('selected'));
this.classList.add('selected');
});
});
}
}
completedLine.addEventListener('click', umClick);
function removeSelected() {
// teste
const listaSelected = document.querySelectorAll('.selected');
for (let i = 0; i < listaSelected.length; i += 1) {
listaSelected[i].remove();
}
}
.lista:focus {
background: red;
}
<!DOCTYPE html>
<html>
<head>
<link rel='stylesheet' href='style.css'>
</head>
<body>
<header>
<h1>My List</h1>
</header>
<input id='texto-tarefa' type="text" />
<button id='criar-tarefa' type="submit" onClick='click()'>Add</button>
<ol id='lista-tarefas'>
</ol>
<button id='remover-selecionado' type="submit" onClick='removeSelected()'>Remove Selected (Only One)</button>
<script src="script.js"></script>
</body>
</html>
But how can i make the class be add, just in the first click, not in the second?
I think you got off on the wrong foot in programming this.
Here is the way I use, may it inspire you.
const
textoTarefa = document.getElementById('texto-tarefa')
, criarTarefa = document.getElementById('criar-tarefa')
, removerSelec = document.getElementById('remover-selecionado')
, listaTarefas = document.getElementById('lista-tarefas')
;
var li_selected = null
;
textoTarefa.oninput = () =>
{
criarTarefa.disabled = (textoTarefa.value.trim().length ===0 )
}
criarTarefa.onclick = () =>
{
listaTarefas.appendChild( document.createElement('li')).textContent = textoTarefa.value.trim()
textoTarefa.value = ''
textoTarefa.focus()
criarTarefa.disabled = true
}
listaTarefas.onclick = ({target}) =>
{
if (!target.matches('li')) return
if (!!li_selected && li_selected !== target ) li_selected.classList.remove('listaSelect')
li_selected = target.classList.toggle('listaSelect') ? target : null
removerSelec.disabled = !li_selected
}
removerSelec.onclick = () =>
{
listaTarefas.removeChild(li_selected)
li_selected = null
removerSelec.disabled = true
}
.listaSelect {
background: #ff0000c4;
}
ol#lista-tarefas {
cursor : pointer
}
<input id='texto-tarefa' type="text" value="">
<button id='criar-tarefa' disabled>Add</button>
<button id='remover-selecionado' disabled>Remove Selected</button>
<ol id='lista-tarefas'></ol>
You were unnecessarily adding an event listener to each item in the list.
You can check the updated fiddle here: https://jsfiddle.net/msa9v2nf/
Since you're already checking which target element is clicked, there isn't any need to add an individual listener to each child item in the list.
I updated the umClick function:
function umClick(event) {
if (event.target.tagName === 'LI') {
const listas = document.querySelectorAll('.lista');
listas.forEach((i) => {
listas.forEach((j) => j.classList.remove('selected'));
event.target.classList.add('selected');
});
}
}
The problem is you call the function umClick and call the function to add .selected within a click event in the same function umClick.
What happens is the click event completedLine.addEventListener('click', umClick); happens before the i.addEventListener('click', function semNomeDois() event. This is why you need a first click on the ol tag for only the first time.
To fixes this you have multiple options:
instead of calling click event on ol tag you can call mousedown which happens before click event.
Calling a click event on the li elements on creation, which needs a new function.
Depending on Vektor's answer, you can remove the unnecessary click event inside the first click event.
Also, I've made the red highlight on the .selected class instead of :focus, just to make it clear when the item is selected.
.selected {
background: red;
}
First Solution
const textoTarefa = document.getElementById('texto-tarefa');
const criarTarefa = document.getElementById('criar-tarefa');
const listaTarefas = document.getElementById('lista-tarefas');
criarTarefa.onclick = function click() {
const lista = document.createElement('li');
lista.className = 'lista';
lista.id = 'lista';
lista.tabIndex = '0';
lista.innerHTML = textoTarefa.value;
listaTarefas.appendChild(lista);
document.body.appendChild(listaTarefas);
textoTarefa.value = '';
};
const completedLine = document.querySelector('ol');
function umClick(event) {
if (event.target.tagName === 'LI') {
const listas = document.querySelectorAll('.lista');
listas.forEach((i) => {
i.addEventListener('click', function semNomeDois() {
listas.forEach((j) =>{
if(j != event.target)
j.classList.remove('selected');
});
this.classList.add('selected');
});
});
}
}
completedLine.addEventListener('mousedown', umClick);
function removeSelected() {
// teste
const listaSelected = document.querySelectorAll('.selected');
for (let i = 0; i < listaSelected.length; i += 1) {
listaSelected[i].remove();
}
}
.selected {
background: red;
}
<!DOCTYPE html>
<html>
<head>
<link rel='stylesheet' href='style.css'>
</head>
<body>
<header>
<h1>My List</h1>
</header>
<input id='texto-tarefa' type="text" />
<button id='criar-tarefa' type="submit" onClick='click()'>Add</button>
<ol id='lista-tarefas'>
</ol>
<button id='remover-selecionado' type="submit" onClick='removeSelected()'>Remove Selected (Only One)</button>
<script src="script.js"></script>
</body>
</html>
Second Solution
const textoTarefa = document.getElementById('texto-tarefa');
const criarTarefa = document.getElementById('criar-tarefa');
const listaTarefas = document.getElementById('lista-tarefas');
criarTarefa.onclick = function click() {
const lista = document.createElement('li');
lista.className = 'lista';
lista.id = 'lista';
lista.tabIndex = '0';
lista.innerHTML = textoTarefa.value;
listaTarefas.appendChild(lista);
lista.addEventListener('click',function(){
itemClick(this);
});
document.body.appendChild(listaTarefas);
textoTarefa.value = '';
};
function itemClick(item) {
const listas = document.querySelectorAll('.lista');
listas.forEach((j) =>j.classList.remove('selected'));
item.classList.add('selected');
}
function removeSelected() {
// teste
const listaSelected = document.querySelectorAll('.selected');
for (let i = 0; i < listaSelected.length; i += 1) {
listaSelected[i].remove();
}
}
.selected {
background: red;
}
<!DOCTYPE html>
<html>
<head>
<link rel='stylesheet' href='style.css'>
</head>
<body>
<header>
<h1>My List</h1>
</header>
<input id='texto-tarefa' type="text" />
<button id='criar-tarefa' type="submit" onClick='click()'>Add</button>
<ol id='lista-tarefas'>
</ol>
<button id='remover-selecionado' type="submit" onClick='removeSelected()'>Remove Selected (Only One)</button>
<script src="script.js"></script>
</body>
</html>
I am not fully understand your problem but,
If you want to add the style when selecting a item, just add the style to
.selected
If you want in focus, and remove the class when there is no focus, you may add an eventlistener to control that.

Why is my save function not executing in its entirety?

I am trying to create a to-do list in HTML, CSS and pure JS.
const dSubmit = document.getElementById('submit');
const storeData = [];
let typer = document.getElementById('type');
let input = document.getElementById('text');
const list = document.getElementById('listHolder');
dSubmit.addEventListener("click", (e) => {
e.preventDefault();
if (input.value == "") {
typer.innerHTML = "Please enter a task";
} else {
typer.innerHTML = "";
store();
}
});
function store() {
const tData = document.getElementById('text').value;
storeData.push(tData);
updater();
input.value = "";
}
function deleter (index) {
storeData.splice(index, 1);
updater();
}
function updater() {
let htmlCode = "";
storeData.forEach(function(item, index){
htmlCode += "<div class='test'><div id = "+ index +">" + item + "</div><div class='sideBtn'><button type='button' class='edit' onClick= 'editF("+ index +")'>Edit</button><button class='delBtn' onClick= 'deleter("+ index +")'>Delete</button> </div> </div>"
})
list.innerHTML = htmlCode;
}
function editF (index) {
let tempOne = document.getElementById(index);
let tempTwo = "<input id='inputText"+String(index)+"' type='text' name='task' value ='" + String(storeData[index]) + "'><button id='saveText"+String(index)+"' onClick= 'save("+index+")' >Save</button>"
tempOne.innerHTML = tempTwo;
}
function save (index) {
console.log('test1')
let tempOne= document.getElementById('saveText'+String(index));
let tempTwo = document.getElementById('inputText'+String(index));
console.log('test2')
tempOne.addEventListener("click", function foo (){
console.log('test3')
storeData.splice(index,1,tempTwo.value)
updater()
}
)
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8">
<title>To Do List</title>
</head>
<body>
<h1>To-do-list</h1>
<form>
<label for="task">Please enter item:</label>
<input type="text" name="task" id="text">
<button id="submit">Submit</button>
</form>
<div id='type'></div>
<div>List:</div>
<div id="listHolder" class="test"></div>
<script type="text/javascript" src="script.js"></script>
</body>
</html>
I am facing problems with the save function. If I edit an item in the to-do list and click the save button, the function executes up to the point of console.log('test2'). If I click save again the function executes in its entirety.
I would like to ask why the first click results in execution of the save function up to 'test2'?
Additionally would anyone be kind enough to critique my JS? are there things in dire need of improvement? or is there a more practical/efficient method of writing my JS code?
Thank you for your help in advance.
After the 'test2' log, you are adding an event listener, and the rest of the code is inside of the listener block. The code in the listener block is only executed once that listener receives a 'click' event, which is why it works the second time.

Having trouble with ToDo List App with saving to localStorage

Super new to all of this so this might be some beginner troubleshooting. The list seems to be working where I'm adding a list element to the UL with a checkbox and delete button. When checkbox is checked it puts a line through the text and when the delete button is clicked it deletes the list element. The assignment asks to save to localStorage so that when refreshed, the list items still remain, and I'm getting super confused by this. What I have now seems to be saving my list elements to an array but I don't understand how to get them to save and stay on the page.
const form = document.querySelector('form');
const input = document.querySelector('#todoInput');
const newElement = document.querySelector('ul');
const savedToDos = JSON.parse(localStorage.getItem('todos')) || [];
newElement.addEventListener('click', function(e) {
if(e.target.tagName === 'BUTTON') {
e.target.parentElement.remove()
}
})
function addToList(text) {
const li = document.createElement('li');
const checkbox = document.createElement('input');
const button = document.createElement('button');
button.innerText = "Delete";
checkbox.type = 'checkbox';
checkbox.addEventListener('change', function() {
li.style.textDecoration = checkbox.checked ? 'line-through' : 'none';
})
li.innerText = text;
li.insertBefore(checkbox, li.firstChild);
li.appendChild(button);
return li;
};
form.addEventListener('submit', function(e) {
e.preventDefault();
const newListItem = addToList(input.value);
input.value = '';
newElement.append(newListItem);
savedToDos.push(newListItem.innerText);
localStorage.setItem('todos', JSON.stringify(savedToDos));
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ToDo App</title>
<link rel="stylesheet" href="app.css">
</head>
<body>
<div>
<h1>Todo List</h1>
<form action="">
<input type="text" id="todoInput" placeholder="Add To Todo List">
<button class="add-button">Add</button>
</form>
<ul id="todoList">
</ul>
</div>
<script src="app.js"></script>
</body>
</html>
It looks like you're failing to populate the DOM when the page loads.
After you retrieve the items from local storage (which you're already doing), loop through the list and add each of them to the DOM:
// After this line, which you've already written:
const savedToDos = JSON.parse(localStorage.getItem('todos')) || [];
// Loop through savedToDos, and for each one, insert a new list:
savedToDos.forEach(function(value) {
const newListItem = addToList(value);
newElement.append(newListItem);
});
Every browser has local storage where we can store data and cookies. just go to the developer tools by pressing F12, then go to the Application tab. In the Storage section expand Local Storage.
this piece of code might help you
// Store Task
function storeTaskInLocalStorage(task) {
let tasks;
if(localStorage.getItem('tasks') === null){
tasks = [];
} else {
tasks = JSON.parse(localStorage.getItem('tasks'));
}
tasks.push(task);
localStorage.setItem('tasks', JSON.stringify(tasks));
}

Using Javascript, I am trying to persist the data in SessionStorage and localStorage of browser but after refreshing the page the data is lost

I am adding array of movie objects to session storage in below line as :
sessionStorage.setItem('MyMovieList', JSON.stringify(movies));
Could someone figure out what is wrong in the code because of which session storage is not working as expected?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width", initial-scale=2.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Putting User input into JS Objects</title>
<style>
</style>
</head>
<body>
<form>
<div class="formBox">
<label for="title">Movie</label>
<input type="text" id="title" placeholder="Title"/>
</div>
<div class="formBox>
<label for="yr">Year</label>
<input type="number" id="yr" placeholder="Year"/>
</div>
<div class="formBox">
<button id="btn">Click to Add</button>
</div>
<div id="msg">
<pre></pre>
</div>
</form>
<script>
let movies = [];
const addMovie = (ev) => {
ev.preventDefault();//to stop the form submission
let movie = {
id: Date.now(),
title: document.getElementById('title').value,
year: document.getElementById('yr').value
}
movies.push(movie);
document.forms[0].reset();// to clear the form for next entry
//document.querySelector('form').reset();
//for display purpose only
console.warn('added', {movies} );
let pre = document.querySelector('#msg pre');
pre.textContent = '\n' + JSON.stringify(movies, '\t', 2);
//saving to localStorage
//localStorage.setItem('MyMovieList', JSON.stringify(movies ));
//saving to sessionStorage
sessionStorage.setItem('MyMovieList', JSON.stringify(movies));
}
document.addEventListener('DOMContentLoaded', () => {
document.getElementById('btn').addEventListener('click',addMovie);
});
</script>
</body>
</html>
So I copied your code and ran it locally and everything worked fine, the movies are stored in the section. The problem you are having is that
`let pre = document.querySelector('#msg pre');
pre.textContent = '\n' + JSON.stringify(movies, '\t', 2);`
is inside the clickHandler so on refresh if you want to see what you had in you session you can just click the button and it will be displayed within the pre tag again. try this instead
<script>
let movies = [];
const addMovie = (ev) => {
ev.preventDefault();//to stop the form submission
let movie = {
id: Date.now(),
title: document.getElementById('title').value,
year: document.getElementById('yr').value
}
movies.push(movie);
document.forms[0].reset();// to clear the form for next entry
//document.querySelector('form').reset();
//for display purpose only
console.warn('added', { movies });
//saving to localStorage
//localStorage.setItem('MyMovieList', JSON.stringify(movies ));
//saving to sessionStorage
sessionStorage.setItem('MyMovieList', JSON.stringify(movies));
let pre = document.querySelector('#msg pre');
pre.textContent = '\n' + sessionStorage.getItem('MyMovieList'),
'\t', 2;
}
document.addEventListener('DOMContentLoaded', () => {
document.getElementById('btn').addEventListener('click', addMovie);
let pre = document.querySelector('#msg pre');
pre.textContent = '\n' + sessionStorage.getItem('MyMovieList'),
'\t', 2;
});
In the addMovie function just save to the sessioStorage and display to again, meanwhile on refresh you the DOMContentLoaded just display whatever you had in the sessionStore.
NB - you might want to format the output the way you want and maybe refactor the duplicate code into a function to make it DRY, I hope this suggestions is useful and can unblock you so that you can continue building your amazing app. #cheers

Using HTML5 local storage to store list items <ul>

I am creating a basic to-do list and was wondering on how to store my list so that when a user comes back to the page or accidentally refreshes the browser window, the list will still be available?
html
<!DOCTYPE html>
<html>
<head>
<title>My To-Do List</title>
<link rel="stylesheet" href="css/styles.css" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
<link rel="stylesheet" href="css/font-awesome-animation.min.css">
<link href='https://fonts.googleapis.com/css?family=Oswald:400,300,700' rel='stylesheet' type='text/css'>
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">
<link rel="icon" href="/favicon.ico" type="image/x-icon">
</head>
<body>
<div id="page">
<header>
<img src="images/checklist.png" alt="some_text">
</header>
<h2>MY TO-DO LIST</h2>
<ul id="sortable"></ul>
<form id="newItemForm">
<input type="text" id="itemDescription" placeholder="Add Description" maxlength="40" />
<input type="submit" id="add" value="add" />
<div id="double">Drag and drop to rearrange items
<br />Click on an item to remove it</div>
</form>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="js/main.js"></script>
<script src="js/sort.js"></script>
<script src="jquery-ui/jquery-ui.js"></script>
</body>
</html>
JavaScript/jQuery
$(function () {
var $list;
var $newItemForm;
var $newItemButton;
var item = '';
$list = $('ul');
$newItemForm = $('#newItemForm');
$newItemButton = $('#newItemButton');
// ADDING A NEW LIST ITEM
$newItemForm.on('submit', function (e) {
e.preventDefault();
var text = $('input:text').val();
$list.append('<li>' + text + '</li>');
$('input:text').val('');
});
$list.on('click', 'li', function () {
var $this = $(this);
var complete = $this.hasClass('complete');
if (complete === true) {
$this.animate({}, 500, 'swing', function () {
$this.remove();
});
} else {
item = $this.text();
$this.remove();
}
});
});
localStorage.setItem($list);
//add animations when you learn how to...
You need to keep the data in an object also. Currently its only in DOM. Everything you add a new todo or edit an existing todo, you need to save that to the localstorage. Storing DOM nodes to localStorage wont work. localStorage also only accept string values.
So this is how I would change your code:
// localStorage key
var lsKey = 'TODO_LIST';
// keeping data
var todoList = {};
function getSavedData () {
var fromLs = localstorage.getItem( lsKey );
if ( !! fromLs ) {
todoList = JSON.parse( fromLs );
} else {
todoList = {};
localstorage.setItem( lsKey, todoList );
};
};
function saveData () {
var stringify = JSON.stringify( todoList );
localstorage.setItem( lsKey, todoList );
};
$newItemForm.on('submit', function(e) {
e.preventDefault();
var text = $('input:text').val().trim(),
uuid = new Date.now();
// lets use input[type:checkbox] to determine if complete or not
if ( !! text ) {
todoList[uuid] = text;
$list.append('<li><input type="checkbox" id=' + uuid + ' /> ' + text + '</li>');
$( 'input:text' ).val( '' );
};
};
$list.on('change', 'li input', function() {
var uuid = $(this).attr( 'id' ),
$li = $(this).parent();
if ( $(this).prop('checked') ) {
todoList[uuid] = undefined;
delete todoList[uuid];
saveData();
$li.fadeOut("slow", function() {
$this.remove();
};
};
});
Good luck, have fun!
You have to do 2 things: first is to store ony your data, not html. Second thing is that you have to provide a name for your item in localStorage because this is a key/value storage, so it needs a name for a key. Also because localStorage stores all data as a string value, call JSON.stringify() on your data before you sore it. So your code will be something like this: localStorage.setItem("yourKeyName", JSON.stringify(yourDataObj)). And when you want to read your data from it do JSON.parse(localStorage.getItem("yourKeyName")) to get your data as json object

Categories

Resources