I'm creating a ToDo app in vanilla JavaScript.
My goal is to be able to remove the input data from local storage when the corresponding "X" button has been clicked.
So far, when you click on an X button, the corresponding input field and checkbox are removed from the DOM with this function I created -
function removeToDoInput(button, input1, input2, input3) {
if (allToDoInputs.length > 2) {
button.addEventListener("click", () => {
toDoInputContainer.removeChild(input1);
toDoInputContainer.removeChild(input2);
toDoInputContainer.removeChild(input3);
for (let toDoInput = 0; toDoInput < allToDoInputs.length; toDoInput++) {
for (let i = 0; i < localStorage.length; i++) {
localStorage.removeItem("ToDo " + toDoInput);
console.log("test");
}
}
})
}
}
This works fine. But like I mentioned I also need to remove the corresponding input data from local storage.
Here is the 'add-to-do' button functionality. You'll notice the removeToDoInput is called which I don't think is good -
function createToDoInput() {
const newToDoInputCheckbox = document.createElement("INPUT");
newToDoInputCheckbox.setAttribute("type", "checkbox");
const newToDoInput = document.createElement("INPUT");
const newXBtn = document.createElement("SPAN");
toDoInputContainer.appendChild(newToDoInputCheckbox);
toDoInputContainer.appendChild(newToDoInput);
toDoInputContainer.appendChild(newXBtn);
newToDoInputCheckbox.classList.add("checkbox");
newToDoInput.classList.add("to-do-input");
newXBtn.classList.add("X");
newXBtn.innerHTML = "X";
newXBtn.addEventListener("click", removeToDoInput(newXBtn, newToDoInputCheckbox, newToDoInput, newXBtn));
}
And here is the save button functionality -
function saveToDoInputs() {
localStorage.setItem("Title", toDoListTitle.value.trim());
for (let toDoInput = 0; toDoInput < allToDoInputs.length; toDoInput++) {
if (createToDoInput) {
localStorage.setItem("ToDo " + toDoInput, allToDoInputs[toDoInput].value.trim());
}
}
}
Both of these last functions I mentioned are attached to buttons through click event listeners.
How can I delete the input from local storage as well as the DOM?
This is incorrect and will be called immediately you assign the event handler
newXBtn.addEventListener("click", removeToDoInput(newXBtn, newToDoInputCheckbox, newToDoInput, newXBtn));
you need
newXBtn.addEventListener("click", function() { removeToDoInput(newXBtn, newToDoInputCheckbox, newToDoInput, newXBtn)});
or similar
I have redesigned your code and now it
wraps the elements in their own container
delegates click and change to the list container
use relative addressing (closest)
gives the todo a unique ID and stores it in an object
retrieves the object from local storage when loading
adds the object to localStorage when changed
I also added a select all and delete selected
NOTE I have commented the localStorage interaction out because stacksnippets do not allow access to localStorage. Just uncomment them on your page
let todos = {};
// const saveTodos = localStorage.getItem("ToDo");
// if (saveTodos) todos = JSON.parse(saveTodos);
const toDoInputContainer = document.getElementById("toDoInputContainer");
const toggleSelDel = () => {
document.getElementById("seldel").classList.toggle("hide",Object.keys(todos).length===0); // show if there are actual entries - we can toggle on existense of checkboxes instead
}
const saveAndToggle = (id,why) => {
// localStorage.setItem("ToDo",JSON.stringify(todos));
console.log(id, why);
toggleSelDel()
};
const saveTodo = (id, text) => {
todos[id] = text;
saveAndToggle(id,"added")
};
const removeTodo = id => {
if (todos[id]) {
delete todos[id];
}
saveAndToggle(id,"deleted"); // toggle anyway
};
function createToDoInput() {
const todoContainer = document.createElement("div");
todoContainer.id = 'todo' + new Date().getTime(); // unique ID
const newToDoInputCheckbox = document.createElement("INPUT");
newToDoInputCheckbox.setAttribute("type", "checkbox");
const newToDoInput = document.createElement("INPUT");
const newXBtn = document.createElement("SPAN");
todoContainer.appendChild(newToDoInputCheckbox);
todoContainer.appendChild(newToDoInput);
todoContainer.appendChild(newXBtn);
newToDoInputCheckbox.classList.add("checkbox");
newToDoInput.classList.add("to-do-input");
newXBtn.classList.add("X");
newXBtn.innerHTML = "X";
toDoInputContainer.appendChild(todoContainer);
}
toDoInputContainer.addEventListener("click", function(e) {
const tgt = e.target;
if (tgt.classList.contains("X")) {
const parent = tgt.closest("div")
removeTodo(parent.id);
parent.remove();
}
});
toDoInputContainer.addEventListener("change", function(e) {
const tgt = e.target;
if (tgt.classList.contains("to-do-input")) {
const id = tgt.closest("div").id;
if (tgt.value === "") removeTodo(id);
else saveTodo(id, tgt.value);
}
});
document.getElementById("add").addEventListener("click", createToDoInput);
toggleSelDel(); // possibly show the select all button
document.getElementById("selAll").addEventListener("click", function() {
const checked = this.checked;
[...toDoInputContainer.querySelectorAll("[type=checkbox]")].forEach(chk => chk.checked = checked)
});
document.getElementById("delAll").addEventListener("click", function() {
[...toDoInputContainer.querySelectorAll("[type=checkbox]:checked")].forEach(chk => {
const parent = chk.closest("div");
removeTodo(parent.id);
parent.remove();
});
});
.hide { display: none; }
<button id="add">Add</button>
<div id="seldel" class="hide"><label>Select all<input type="checkbox" id="selAll"/></label><button type="button" id="delAll">Delete selected</button></div>
<div id="toDoInputContainer"></div>
Related
I'm trying to delete the form input posted onto the DOM, but the removeChild code isn't functioning, When inspecting the console I will be given an error which is: materialize.min.js:6 Uncaught TypeError: Cannot read property 'M_Modal' of null at HTMLBodyElement.value (materialize.min.js:6)
// ----------------Models Materialize Framework----------------
document.addEventListener('DOMContentLoaded', () => {
var elems = document.querySelectorAll('.modal');
var instances = M.Modal.init(elems);
});
//Delete Exercises
const delExerciseBtn = document.querySelector('.del-exercise-btn');
delExerciseBtn.addEventListener('click', (e) => {
if(e.target.className == 'delete'){
const h6 = e.target.parentElement;
h6.removeChild(e.target);
}
});
// Add User's To the Dom.
const addExerciseDom = document.querySelector('.exercise-dom');
const exerciseForm = document.querySelector('.exercises-form');
exerciseForm.addEventListener('submit', (e) => {
e.preventDefault();
// Get Input Value
const value = exerciseForm.querySelector('input[type="text"]').value;
// Create Elements
const h6 = document.createElement('h6');
// Add Content
h6.textContent = value;
// Append To Dom
addExerciseDom.appendChild(h6);
});
For Multiple Values of h6
Best to add id with each new h6
Check on weight and exercise
// Materialize Initialization Of Autocomplete, Exercise.
document.addEventListener('DOMContentLoaded', () => {
var elems = document.querySelectorAll('.autocomplete');
var instances = M.Autocomplete.init(elems,{
data: {
"Lat Pull Down": null,
"Lat Down": null,
},
limit:2,
minLength:1,
});
});
// Materialize Initialization Of Box Select, Sets and Reps.
document.addEventListener('DOMContentLoaded', () => {
var elems = document.querySelectorAll('select');
var instances = M.FormSelect.init(elems);
});
// Materialize Initialization Of Weights CharacterCount
document.addEventListener('DOMContentLoaded', () => {
var textNeedCount = document.querySelectorAll('.weightcountercount');
M.CharacterCounter.init(textNeedCount);
});
//need to put restrition on the number typed into the box without it submitting
// ----------------Models Materialize Framework----------------
document.addEventListener('DOMContentLoaded', function() {
var elems = document.querySelectorAll('.modal');
var instances = M.Modal.init(elems);
});
// ------------ Add Form's Inputs Onto The HomePage----------------
//-------------Exercises------------
// Delete Exercises From The Dom
const delExerciseBtn = document.querySelector('.del-exercise-btn');
delExerciseBtn.addEventListener('click', (e) => {
// Remove Form Input
const h6_e = document.getElementById('h6_exercise');
h6_e.remove();
// Remove Disable Btn
disabledExersiceBtn.removeAttribute('disabled');
});
// Add User's Exercises To The Dom.
const addExerciseDom = document.querySelector('.exercise-dom');
const exerciseForm = document.querySelector('.exercises-form');
const disabledExersiceBtn = document.querySelector('.disabled-exersicebtn');
exerciseForm.addEventListener('submit', (e) => {
e.preventDefault();
// Get Input Value
const value = exerciseForm.querySelector('input[type="text"]').value;
// Create Elements
// const h6 = document.createElement('h6');
//for exercise
const h6_exercise = document.createElement('h6');
// Add Content
h6_exercise.textContent = value;
//adding id
h6_exercise.setAttribute("id", "h6_exercise");
// Append To Dom
addExerciseDom.appendChild(h6_exercise);
//Disable Btn
disabledExersiceBtn.setAttribute('disabled', 'disabled');
});
//---------------------Weight----------------------
// Delete Exercises From The Dom
const delWeightBtn = document.querySelector('.del-weight-btn');
delWeightBtn.addEventListener('click', (e) => {
// Remove Form Input
let h6_r = document.getElementById('h6_weight');
h6_r.remove();
});
// Add User's Weight To The Dom.
const addWeightDom = document.querySelector('.weight-dom');
const weightForm = document.querySelector('.weight-form');
weightForm.addEventListener('submit', (e) => {
e.preventDefault();
if(document.getElementById('h6_weight'))
{
let h6_r = document.getElementById('h6_weight');
h6_r.remove();
}
// Get Input Value
const value = weightForm.querySelector('input[type="number"]').value;
const value1 = weightForm.querySelector('input[type="text"]').value;
//console.log(value, value1);
// Create Elements
const h6_weight = document.createElement('h6');
h6_weight.setAttribute('id','h6_weight')
//h6.classList.add("center");// not working
// Add Content
h6_weight.textContent = value + " " + value1;
// Append To Dom
addWeightDom.appendChild(h6_weight);
});
// ------------------Add Exercises Colum----------------------
//const addMoreBtn = document.getElementById("addmorebtn");
////const addColums = document.getElementById("addcolumns");
//addMoreBtn.addEventListener('click', (e) => {
// e.preventDefault();
// const text =
// `<div class="col s4 height"></div>
// <div class="col s2 height "></div>
// <div class="col s2 height"></div>
// <div class="col s2 height"></div>
// <div class="col s2 height"></div>`
//const position = "beforeend";
//addColums.insertAdjacentHTML(position, text);
//});
You can do it like this
// ----------------Models Materialize Framework----------------
document.addEventListener('DOMContentLoaded', () => {
var elems = document.querySelectorAll('.modal');
var instances = M.Modal.init(elems);
});
//Delete Exercises
const delExerciseBtn = document.querySelector('.del-exercise-btn');
delExerciseBtn.addEventListener('click', (e) => {
const h6 = document.getElementsByTagName('h6')[0];
h6.remove();
});
// Add User's To the Dom.
const addExerciseDom = document.querySelector('.exercise-dom');
const exerciseForm = document.querySelector('.exercises-form');
const disabledExersiceBtn = document.querySelector('.disabled-exersicebtn');
exerciseForm.addEventListener('submit', (e) => {
e.preventDefault();
// Get Input Value
const value = exerciseForm.querySelector('input[type="text"]').value;
// Create Elements
const h6 = document.createElement('h6');
// Add Content
h6.textContent = value;
// Append To Dom
addExerciseDom.appendChild(h6);
//Disable Btn
disabledExersiceBtn.setAttribute('disabled', 'disabled');
});
I'm trying to use local storage so that my invites stay on the page when refreshed. How would I go about implementing this into my code. I really don't know where to start and I'm really struggling with it. Please cans someone just show me how to implement this into my code. Ive been creating child elements and appending them to the UL in the HTML.
const form = document.getElementById("registrar");
const input = form.querySelector("input");
const mainDiv = document.querySelector(".main");
const ul = document.getElementById("invitedList");
const div = document.createElement('div');
const filterLabel = document.createElement('label');
const filterCheckBox = document.createElement('input');
filterLabel.textContent = "Hide those who havent responded";
filterCheckBox.type = 'checkbox';
div.appendChild(filterLabel);
div.appendChild(filterCheckBox);
mainDiv.insertBefore(div, ul);
/*
This creates a checkbox to see you has confirmed if they are coming
to the event or not.
*/
filterCheckBox.addEventListener("change", (e) => {
const isChecked = e.target.checked;
const lis = ul.children;
if (isChecked) {
for (let i = 0; i < lis.length; i += 1) {
let li = lis[i]
if (li.className === 'responded') {
li.style.display = '';
} else {
li.style.display = 'none';
}
}
} else {
for (let i = 0; i < lis.length; i += 1) {
let li = lis[i]
li.style.display = '';
}
}
});
/*
This function creates new list items (the invites).
*/
createLi = (text) => {
createElement = (elementName, property, value) => {
const element = document.createElement(elementName);
element[property] = value;
return element;
}
appendElement = (elementName, property, value) => {
const element = createElement(elementName, property, value);
li.appendChild(element);
return element;
}
const li = document.createElement("li");
appendElement("span", "textContent", text);
appendElement("label", "textContent", "Confirmed")
.appendChild(createElement("input", "type", "checkbox"));
appendElement("button", "textContent", "edit");
appendElement("button", "textContent", "remove");
return li;
}
}
form.addEventListener("submit", (event) => {
event.preventDefault();
const text = input.value;
input.value = "";
const li = createLi(text);
ul.appendChild(li);
});
ul.addEventListener("change", () => {
const checkbox = event.target;
const checked = checkbox.checked;
const listItem = checkbox.parentNode.parentNode;
if (checked) {
listItem.className = "responded";
} else {
listItem.className = "";
}
});
ul.addEventListener("click", (e) => {
if (e.target.tagName === 'BUTTON') {
const button = e.target;
const li = button.parentNode;
const ul = li.parentNode;
const action = button.textContent;
const nameActions = {
remove: () => {
ul.removeChild(li);
},
edit: () => {
const span = li.firstElementChild;
const input = document.createElement('input');
input.type = 'text';
input.value = span.textContent;
li.insertBefore(input, span);
li.removeChild(span);
button.textContent = 'Save';
},
Save: () => {
const input = li.firstElementChild;
const span = document.createElement('span');
span.textContent = input.value;
li.insertBefore(span, input);
li.removeChild(input);
button.textContent = 'edit';
}
};
nameActions[action]();
}
});
I only can give you an idea for this, if you are planning to save any big data to localStorage, you can save your data to string and remake it to JSON object!
Here's an exmaple for saving data set
var toSave = [ ];
var whatToSave = (Your invites data List, maybe a "text" var for your code?, organize
to array or loop for your "UL" tag)
for(var i=0;i<whatToSave.length;i++){
var obj = [];
obj[i] = {
text:whatToSave[i].text,
data1:whatToSave[i].data1,
data2:whatToSave[i].data2,
...
}
toSave.push(obj[i]);
}
var saveToString = JSON.stringify(toSave);
localStorage.setItem('invites', saveToString); //your invite data saved in local
storage
....
//after refreshing or when you need to use your saved data
var cameBack = JSON.parse(localStorage.getItem('invites'));
//this will return same data again
//and make function to make UL using retured data array
you don't really need to for loop and push if you already organized needed data to array, just wrote it to make you understand an idea to save data in localStorage. I don't know if this will help you but get an idea and implement it to your project :)
I have this function:
function test() {
var a = document.getElementById('test');
var b = document.createElement('input');
b.type = 'checkbox';
b.addEventListener( 'change', function() {
if(this.checked) {
//do something and save the state (checked)
} else {
//do something else and save the state(not checked)
}
}
and I want to save the state of the checkbox on localstorage from the appended checkbox, what is the best way to do it?
so if you are appending multiple checkboxes and you want to probably set all of their data separately you are going to need to have a different id for each checkbox so when you append a new one I would increment the ids
//function that appends new checkbox
var i = 0;
function appendCheckBox(){
i++
checkBoxId = 'checkbox' + i;
document.getElementById('checkbox-container').innerHTML = `<input type="checkbox" id="${checkBoxId}" onclick="handleCheckBoxClick(this)"></input>`;
}
//function to handle checkbox click you would probably want to make it check if
//the checkbox is already checked if it is set value to unchecked
function handleCheckBoxClick(ev){
var checkBoxId = ev.id;
localStorage.setItem(checkBoxId, 'checked')
}
You can use :
Setting storage :
localStorage.setItem('checkbox', b.checked);
Getting storage :
var checkVal=localStorage.getItem('checkbox');
You'll have to do a bit of work. localStorage doesn't work in a snippet here, so a working example of the example code can be found # this JSFiddle
localStorage.setItem("Checkboxes", "{}");
const showCurrentCheckboxStates = () =>
document.querySelector("pre").textContent = `Checkboxes saved state ${
JSON.stringify(JSON.parse(localStorage.getItem("Checkboxes")), null, " ")}`;
const saveCheckboxState = (val, id) => {
localStorage.setItem("Checkboxes",
JSON.stringify({
...JSON.parse(localStorage.getItem("Checkboxes")),
[`Checkbox ${id}`]: val })
);
showCurrentCheckboxStates();
};
const createCheckbox = id => {
let cb = document.createElement('input');
cb.type = 'checkbox';
cb.dataset.index = id;
cb.title = `Checkbox ${id}`;
document.body.appendChild(cb);
// save the initial state
saveCheckboxState(0, id);
};
document.addEventListener("click", evt =>
evt.target.dataset.index &&
saveCheckboxState(+evt.target.checked, evt.target.dataset.index)
);
for (let i = 1; i < 11; i += 1) {
createCheckbox(i);
}
Trying to figure out how I can get a count to work, after a user has entered a movie and submitted it so that the element would be created in a table.
There would be a button in the created element. How can I get make button increment a count that would also show up in the created element.
class Movie {
constructor(title, year, cost) {
this.title = title;
this.year = year;
this.cost = cost;
}
}
class UI {
createMovie(movie) {
const list = document.getElementById('movie-list');
let row = document.createElement('tr');
row.innerHTML = `
<td>${movie.title}</td>
<td>${movie.year}</td>
<td>${movie.cost}</td>
<td class="count"><td>
<input type="submit" value="Watched" class="btn2"></input>
<td>X</td>
`;
list.appendChild(row);
}
getMovie() {
}
addMovieWatchCount() {
document.querySelector('.btn2').addEventListener('click', function(e) {
let count = 0
let numOfMovie = document.querySelector('.count').innerHTML = count;
numOfMovie.appendChild(count);
});
}
deleteMovie(target) {
if(target.className === 'delete') {
target.parentElement.parentElement.remove();
}
}
clearMovieFields() {
document.getElementById('movie').value = '';
document.getElementById('year').value = '';
document.getElementById('cost').value = '';
}
}
//Event Handling
document.getElementById('form').addEventListener('submit', function(e) {
const title = document.getElementById('movie').value;
const year = document.getElementById('year').value;
const cost = document.getElementById('cost').value;
const movie = new Movie(title, year, cost);
const ui = new UI();
ui.createMovie(movie);
ui.clearMovieFields();
e.preventDefault();
});
document.getElementById('movie-list').addEventListener('click', function(e) {
const ui = new UI();
ui.deleteMovie(e.target);
e.preventDefault();
});
The user clicks the "Watched" button in created 'input' element, in the table and it should increment the content of a created 'th' element to 1, and so on.
Try generating a unique per-movie class for each <tr> (like btn2-${movie.title}) added in createMovie and then also take a movie parameter in addMovieWatchCount to specifically bind a click listener to that specific button instead of just the first matching element.
I have a simple TODO app written in vanilla javascript. Here is the application:
Issue/Problem that I am having at this point is:
When I click New todo button the existing checked state of the checkbox disappears.
I am not sure how to persist the checkbox state after prompt window OK click. Please find the source code below.
const classNames = {
TODO_ITEM: 'todo-container',
TODO_CHECKBOX: 'todo-checkbox',
TODO_TEXT: 'todo-text',
TODO_DELETE: 'todo-delete',
}
const checkbox = document.createElement( "input" );
checkbox.type = "checkbox"
checkbox.id = classNames.TODO_CHECKBOX
const list = document.getElementById('todo-list')
const itemCountSpan = document.getElementById('item-count')
const uncheckedCountSpan = document.getElementById('unchecked-count')
function newTodo() {
let newTodo = prompt("Please enter a todo item");
if(newTodo){
itemCountSpan.innerHTML = parseInt(itemCountSpan.innerHTML) + 1
list.append(checkbox)
list.innerHTML += "<li>" + newTodo
}
let allCheckBoxes = document.querySelectorAll("input[id='todo-checkbox']");
uncheckedCountSpan.innerHTML = allCheckBoxes.length
console.log(allCheckBoxes.length)
for(let i = 0; i < allCheckBoxes.length; i++){
allCheckBoxes[i].onclick = function() {
if ( this.checked ) {
uncheckedCountSpan.innerHTML = parseInt(uncheckedCountSpan.innerHTML) - 1
}
else {
uncheckedCountSpan.innerHTML = parseInt(uncheckedCountSpan.innerHTML) + 1
}
};
}
}
Please let me know if you have any thoughts/directions.
Thanks in advance.
You have two issues: first, you're appending the same checkbox every time. Second, you are directly editing innerHTML, which is forcing the DOM to re-render everything, reverting the state of the inputs. Here's how I would do it:
const classNames = {
TODO_ITEM: 'todo-container',
TODO_CHECKBOX: 'todo-checkbox',
TODO_TEXT: 'todo-text',
TODO_DELETE: 'todo-delete',
}
const list = document.getElementById('todo-list')
const itemCountSpan = document.getElementById('item-count')
const uncheckedCountSpan = document.getElementById('unchecked-count')
function newTodo() {
let newTodo = prompt("Please enter a todo item");
if(!newTodo){
return
}
itemCountSpan.innerHTML = parseInt(itemCountSpan.innerHTML) + 1
uncheckedCountSpan.innerHTML = parseInt(uncheckedCountSpan.innerHTML) + 1
const checkbox = document.createElement( "input" );
checkbox.onclick = function() {
if ( this.checked ) {
uncheckedCountSpan.innerHTML = parseInt(uncheckedCountSpan.innerHTML) - 1
}
else {
uncheckedCountSpan.innerHTML = parseInt(uncheckedCountSpan.innerHTML) + 1
}
};
checkbox.type = "checkbox"
checkbox.class = classNames.TODO_CHECKBOX
list.append(checkbox)
const listItem = document.createElement("li")
listItem.innerHTML = newTodo
list.append(listItem)
}
Note that I also replaced the id of the checkbox with a class - there are multiple checkboxes, and ids should be unique.