I am making a todo app. In the app, there are delete and edit functions. But the Delete function works at the first todo. If I try other todos, It deletes a lot of todos. I tried many ways but disn't work. Please help!
const todoForm = document.querySelector(".todo-form");
const todos = document.querySelector(".todos");
const todoTitle = document.querySelector(".todo-title");
const modal = document.querySelector("#modal");
const modalClose = document.querySelector(".close-button");
const editedText = document.querySelector(".edited-text ");
const submitEdit = document.querySelector(".submit");
const todoList = [];
let totalTodo = 0;
function findFromArray(id, array) {
for (let i = 0; i < array.length; i++) {
if (id === array[i].id) {
return array[i];
}
}
return null;
}
function deleteTodo(id) {
const element = document.getElementById(id);
element.remove();
const data = findFromArray(id, todoList);
todoList.splice(todoList.indexOf(data), 1);
}
function editTodo(id) {
const element = document.querySelector("#" + id + " h1");
modal.showModal();
modalClose.addEventListener("click", () => {
modal.close();
});
submitEdit.addEventListener("click", () => {
const newTodoText = editedText.value;
element.innerText = newTodoText;
modal.close();
});
}
todoForm.addEventListener("submit", (e) => {
e.preventDefault();
let title = todoTitle.value;
const newTodo = document.createElement("div");
const h1 = document.createElement("h1");
const deleteButton = document.createElement("button");
const editButton = document.createElement("button");
deleteButton.innerText = "Delete";
editButton.innerText = "Edit";
todoList.push({
id: "Todo" + totalTodo,
title,
});
totalTodo++;
for (let i = 0; i < todoList.length; i++) {
newTodo.classList.add("todo-card");
todos.appendChild(newTodo);
h1.innerText = todoList[i].title;
deleteButton.addEventListener("click", () => deleteTodo(todoList[i].id));
editButton.addEventListener("click", () => editTodo(todoList[i].id));
newTodo.appendChild(h1);
newTodo.appendChild(deleteButton);
newTodo.appendChild(editButton);
newTodo.id = todoList[i].id;
}
todoTitle.value = "";
});
<body>
<h1 class="todo-heading">Todo App</h1>
<form class="todo-form">
<input class="todo-title" type="text" placeholder="Todo Name" />
<input type="Submit" class="todo-button" />
</form>
<br />
<div class="todos"></div>
<dialog id="modal">
<h1>Edit</h1>
<input type="text" class="edited-text" placeholder="New Todo" />
<br />
<br />
<button class="submit">Submit</button>
<button class="close-button">
<svg class="svg-icon" viewBox="0 0 20 20">
<path
d="M10.185,1.417c-4.741,0-8.583,3.842-8.583,8.583c0,4.74,3.842,8.582,8.583,8.582S18.768,14.74,18.768,10C18.768,5.259,14.926,1.417,10.185,1.417 M10.185,17.68c-4.235,0-7.679-3.445-7.679-7.68c0-4.235,3.444-7.679,7.679-7.679S17.864,5.765,17.864,10C17.864,14.234,14.42,17.68,10.185,17.68 M10.824,10l2.842-2.844c0.178-0.176,0.178-0.46,0-0.637c-0.177-0.178-0.461-0.178-0.637,0l-2.844,2.841L7.341,6.52c-0.176-0.178-0.46-0.178-0.637,0c-0.178,0.176-0.178,0.461,0,0.637L9.546,10l-2.841,2.844c-0.178,0.176-0.178,0.461,0,0.637c0.178,0.178,0.459,0.178,0.637,0l2.844-2.841l2.844,2.841c0.178,0.178,0.459,0.178,0.637,0c0.178-0.176,0.178-0.461,0-0.637L10.824,10z"
></path>
</svg>
</button>
</dialog>
I was expecting to remove a todo element and the todo data. But It removes a lot of todo elements and data. I tried the splice method and the delete keyword but didn't work. Please help!
I think the problem is with the for loop you are using to create the new elements. The loop is iterating through the entire todoList array, but it should only be creating a new todo element for the last array item.
todoForm.addEventListener("submit", (e) => {
e.preventDefault();
let title = todoTitle.value;
const newTodo = document.createElement("div");
const h1 = document.createElement("h1");
const deleteButton = document.createElement("button");
const editButton = document.createElement("button");
deleteButton.innerText = "Delete";
editButton.innerText = "Edit";
todoList.push({
id: "Todo" + totalTodo,
title,
});
totalTodo++;
const lastTodo = todoList[todoList.length - 1];
newTodo.classList.add("todo-card");
todos.appendChild(newTodo);
h1.innerText = lastTodo.title;
deleteButton.addEventListener("click", () => deleteTodo(lastTodo.id));
editButton.addEventListener("click", () => editTodo(lastTodo.id));
newTodo.appendChild(h1);
newTodo.appendChild(deleteButton);
newTodo.appendChild(editButton);
newTodo.id = lastTodo.id;
todoTitle.value = "";
});
Also, like Jelmer commented.. In the deleteTodo function, remove todoList =. You should use the splice method directly on the todoList array to remove the element.
todoList.splice(todoList.indexOf(data), 1);
hope you are doing well :)
I am doing an exercise where I have to convert the input value from CM to INCH and from INCH to CM.
when I enter a value for the first time, it works, but when I want to modify the value, it does not want to work.
i get each time the same result even by changing the value.
thanks a lot
<!-- cm to inch -->
<input type="text" placeholder="please insert a number " id="inputNumber" />
<button type="button" onclick="cmToInch()">convert to cm</button>
<button type="button" onclick="inchToCm()">convert to inch</button>
<div id="result"></div>
<script>
const result = document.getElementById("result");
const inputNumber = document.getElementById("inputNumber");
const numValue = inputNumber.value;
const cmToInch = () => {
const div = document.createElement("div");
div.innerText = +numValue * 2.54;
result.appendChild(div);
};
const inchToCm = () => {
const div = document.createElement("div");
div.innerText = +numValue / 2.54;
result.appendChild(div);
};
</script>
I am creating a comment box and I managed to append whatever I type to a div I wanted, however I have added another input and trying to append that along with the comments, however when I do this the second time,it appends both the previous and current comment therefore the previous comment duplicates. I know I'm doing something wrong in my display_commnents function, however I'm not entirely sure what it could be, basically I just want whatever is entered on both title and comments to append on the comment-box with title on top and comment just below. Below is my code:
<div class="container">
<h1>Write New Post</h1>
<form>
<input id="title" type="text" placeholder="Title" value="">
<textarea id="" placeholder="Leave us a comment" value=""></textarea>
<input id="giphy" type="text">
<div class="btn">
<input id="submit" type="submit" value="comment">
<button id="clear">Cancel</button>
</div>
</form>
</div>
<div class="comments">
<h2>Comments</h2>
<div id="comment-box" value="submit">
</div>
</div>
And this is my JS code:
const title = document.querySelector('#title')
const field = document.querySelector('textarea');
const textBackUp = title.getAttribute('placeholder')
const backUp = field.getAttribute('placeholder')
const btn = document.querySelector('.btn');
const clear = document.getElementById('clear')
const submit = document.querySelector('#submit')
// const comments = document.querySelector('#comment-box')
const titleText = document.getElementById('title')
const comments = document.getElementById('comment-box')
let title_arr = [];
let comments_arr = [];
title.onfocus = function(){
this.setAttribute('placeholder', '')
}
title.onblur = function(){
this.setAttribute('placeholder', textBackUp)
}
field.onfocus = function(){
this.setAttribute('placeholder','')
this.style.borderColor = '#333'
btn.style.display = 'block'
} // when clicking on this, placeholder changes into ' ', border colour changes and buttons will appear.
field.onblur = function(){
this.setAttribute('placeholder',backUp)
} //click away, placeholder returns
const display_comments = () => {
let list = '<ul>'
title_arr.forEach(title => {
comments_arr.forEach(comment => {
list += `<li>${title} <br>${comment}`
})
})
list += '</ul>'
comments.innerHTML = list
}
clear.onclick = function(e){
e.preventDefault();
btn.style.display = 'none'
title.value = ''
field.value = ''
display_comments()
}
submit.onclick = function(e){
e.preventDefault();
const head = title.value;
const content = field.value;
if(head.length > 0){
title_arr.push(head)
display_comments();
title.value = '';
}
if(content.length > 0){
comments_arr.push(content)
display_comments();
field.value = '';
}
}
any help would be appreciated
The problem is that you have a double nested loop, producing a Cartesion product of the all the introduced titles and the comments.
To solve this, use only one array for collecting the input, so that title and comment are always kept together in one array entry. Such an entry can be an object with two properties, one for the title, and one for the comment.
Here is your code adapted, just for fixing that issue:
const title = document.querySelector('#title')
const field = document.querySelector('textarea');
const textBackUp = title.getAttribute('placeholder')
const backUp = field.getAttribute('placeholder')
const btn = document.querySelector('.btn');
const clear = document.getElementById('clear')
const submit = document.querySelector('#submit')
// const comments = document.querySelector('#comment-box')
const titleText = document.getElementById('title')
const comments = document.getElementById('comment-box')
let arr = []; // Only one array
title.onfocus = function(){
this.setAttribute('placeholder', '');
}
title.onblur = function(){
this.setAttribute('placeholder', textBackUp);
}
field.onfocus = function(){
this.setAttribute('placeholder','');
this.style.borderColor = '#333';
btn.style.display = 'block';
}
field.onblur = function(){
this.setAttribute('placeholder', backUp);
}
const display_comments = () => {
let list = '<ul>';
// Only one loop -- over objects with two properties
arr.forEach(({head, content}) => {
list += `<li><b>${head}</b><br>${content}`;
})
list += '</ul>';
comments.innerHTML = list;
}
clear.onclick = function(e){
e.preventDefault();
btn.style.display = 'none';
title.value = '';
field.value = '';
display_comments();
}
submit.onclick = function(e){
e.preventDefault();
const head = title.value;
const content = field.value;
// Only one if-block
if(head.length > 0 || content.length > 0){
arr.push({head, content}); // Only one push -- of an object
display_comments();
title.value = '';
field.value = '';
}
}
<div class="container">
<h1>Write New Post</h1>
<form>
<input id="title" type="text" placeholder="Title" value="">
<textarea id="" placeholder="Leave us a comment" value=""></textarea>
<div class="btn">
<input id="submit" type="submit" value="comment">
<button id="clear">Cancel</button>
</div>
</form>
</div>
<div class="comments">
<h2>Comments</h2>
<div id="comment-box" value="submit">
</div>
</div>
I got 2 radio buttons and each one have a function (change the weather from celsius to fahrenheit)
The wearher data is from an API.
<p>
<label>
Celsius
<input
type="radio"
name="weather"
value="celsius"
class="celsius"
onclick="weatherC()"
/>
</label>
</p>
<p>
<label>
Fahrenheit
<input
type="radio"
name="weather"
value="fahrenheit"
class="fahrenheit"
onclick="weatherF()"
/>
</label>
</p>
Those are the functions in JavaScript:
function weatherC() {
fetch("http://api.openweathermap.org/data/2.5/weather?q=Azuga&units=metric&appid=cbf24ef0d0428af6ca69c8320756cbf5")
.then(handleResponse)
.then((data) => {
const maxTemp = document.querySelector(".max-temp");
maxTemp.innerText = Math.ceil(data.main["temp_max"]) + "\u2103";
const minTemp = document.querySelector(".min-temp");
minTemp.innerText = Math.ceil(data.main["temp_min"]) + "\u2103";
const currentTemp = document.querySelector(".current-temp");
currentTemp.innerText = Math.ceil(data.main["temp"]) + "\u2103";
const feelsLike = document.querySelector(".feels-like");
feelsLike.innerText = Math.ceil(data.main["feels_like"]) + "\u2103";
const city = document.querySelector(".city");
city.innerText = data["name"];
const statusIcon = document.querySelector(".iconClass");
const iconFromApi = data.weather[0].icon;
const icon = `http://openweathermap.org/img/wn/${iconFromApi}#2x.png`;
statusIcon.innerHTML = `<img src="${icon}">`;
});
}
function weatherF() {
fetch("http://api.openweathermap.org/data/2.5/weather?q=Azuga&units=fehrenheit&appid=cbf24ef0d0428af6ca69c8320756cbf5")
.then(handleResponse)
.then((data) => {
const maxTemp = document.querySelector(".max-temp");
maxTemp.innerText = Math.ceil(data.main["temp_max"]) + "\u2109";
const minTemp = document.querySelector(".min-temp");
minTemp.innerText = Math.ceil(data.main["temp_min"]) + "\u2109";
const currentTemp = document.querySelector(".current-temp");
currentTemp.innerText = Math.ceil(data.main["temp"]) + "\u2109";
const feelsLike = document.querySelector(".feels-like");
feelsLike.innerText = Math.ceil(data.main["feels_like"]) + "\u2109";
const city = document.querySelector(".city");
city.innerText = data["name"];
const statusIcon = document.querySelector(".iconClass");
const iconFromApi = data.weather[0].icon;
const icon = `http://openweathermap.org/img/wn/${iconFromApi}#2x.png`;
statusIcon.innerHTML = `<img src="${icon}">`;
});
}
weatherC();
how can i make it that when i refresh the page the selected radio button to remain the same?(I m new to js so an example would be awesome :D)
In the weatherF and the weatherC functions you need to put in a localStorage.setItem call
And then in the page load you need to do a check if localStorage.getItem() return null
If local storage is new to you set up a few small apps where you test out the locaStorage.setItem(key, value) and localStorage.getItem(key)
If you set up the localStorage functions and you can’t get it to work post again and can take a look at it. You really should do a bit more of the code though before positing.
I want to display contents in the last <div> element when a click event occurs but now it only shows 1st 2 elements. Is there something I am not doing right somewhere?
Here is my code so far:
JS
const iname = document.getElementById("name");
const iemail = document.getElementById("email");
const iphone = document.getElementById("phone");
const submit = document.getElementById("submit");
const storage = document.getElementById("storage");
submit.onclick = function () {
const name = iname.value;
const email = iemail.value;
const phoneno = iphone.value;
if (name && email && phoneno) {
localStorage.setItem(name, "");
localStorage.setItem(email, "");
localStorage.setItem(phoneno, "");
location.reload();
}
};
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
const value = localStorage.getItem(key);
storage.innerHTML += `Name : ${key}<br />Email : ${value}`;
}
localStorage.clear()
HTML
<p>Name</p>
<input id="name" autocomplete="off">
<p>Email</p>
<input id="email" autocomplete="off">
<p>Phone no</p>
<input id="phone" autocomplete="off">
<button id="submit">Let's go</button>
<div id="storage" class="box">
<h1>Is this correct?</h1></div>
I think you are setting the values in localstorage the wrong way.
The syntax for storing stuff in there is localstorage.setItem(keyName, keyValue).
And your code is setting the keyName argument to the value you are getting from the form and keyValue argument to an empty string; not what you need.
Make the following changes and you should be good to go (see comments):
submit.onclick = function () {
const name = iname.value;
const email = iemail.value;
const phoneno = iphone.value;
if (name && email && phoneno) {
// set local storage values
localStorage.setItem("name", name); // modified
localStorage.setItem("email", email); // modified
localStorage.setItem("phoneno", phoneno); // modified
location.reload();
}
console.log(localStorage); // new (maybe unnecessary)
};
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
const value = localStorage.getItem(key);
storage.innerHTML += `${upFirst(key)}: ${value}<br>`; // modified
}
localStorage.clear();
/**
* new: making the first letter an upper case (for labels in the output div).
* See usage in 'for loop' above.
*/
function upFirst(stringValue) {
return stringValue.slice(0, 1).toUpperCase() + stringValue.slice(1);
}