I have already found a way to get what I want, but I'm trying to understand why the next code doesn't work.
If to be more precise why does the function showHideTaskDetails() doesn't seem to do what it should do (BTW leave aside its name, it's not an indication of its purpose)
I expect the following to happen:
When clicking on the button with the class "fa-solid fa-circle-chevron-down", the value of the variable hideContent change to the opposite of the current value (if it's true to become false and vice versa).
After that if the hideContent is true the variable color will be "background-color: red" so the style of all dives will change to have background with the color red.
But instead, nothing happens!
HTML
<body>
<div class="container">
<form action="">
<label for="task-headline">Task Headline</label>
<input type="text" id="task-headline">
<label for="deadline">Task Deadline</label>
<input type="date" id="deadline">
<label for="task-details">Task Details</label>
<textarea name="" id="task-details" cols="80" rows="10"></textarea>
<button id="add-task">Add Task</button>
</form>
<div class="tasks-area"></div>
</div>
</body>
JS
const headLineEl = document.getElementById("task-headline")
const deadlineEl = document.getElementById("deadline")
const taskDetailsEl = document.getElementById("task-details")
const TasksAreaEl = document.querySelector(".tasks-area")
addTaskBtn = document.getElementById("add-task")
let hideContent = true
let color = ""
showTasks()
addTaskBtn.addEventListener("click", (e) => {
e.preventDefault()
const newTask = collectTaskInfo()
saveToLs(newTask)
showTasks()
})
//get from the local storage the current tasks
function getLsData() {
let currentLsContent = JSON.parse(localStorage.getItem("tasks"))
if (currentLsContent === null) {
currentLsContent = []
}
return currentLsContent
}
//show the tasks on the dom
function showTasks() {
const array = getLsData()
let tasksContainer = []
array.map(task => {
const readyTask =
`
<div class="task-container" style=${color}>
<div class="main-basic-info">
<p> <span>Task:</span> ${task.headline} </p>
<div class="left-part">
<p> <span>Deadline:</span> ${task.deadline} </p>
<i class="fa-solid fa-circle-chevron-down" onClick="showHideTaskDetails()"></i>
</div>
</div>
<p class="details"> <span>Details:</span> ${task.details} </p>
</div>
<br>
`
tasksContainer.push(readyTask)
})
TasksAreaEl.innerHTML = tasksContainer
}
//hide unhide details
function showHideTaskDetails() {
hideContent = !hideContent
console.log(hideContent);
if (hideContent) color = "background-color: red"
// const test = document.getElementsByTagName('div')
// test.style = "background-color: red"
}
//collect task information to object
function collectTaskInfo() {
const obj = {
headline: headLineEl.value,
deadline: deadline.value,
details: taskDetailsEl.value
}
return obj
}
//update the current array in local storage with the new task
function addNewTaskToLsArray(newTask) {
const currentTaskArrayInLs = getLsData()
currentTaskArrayInLs.push(newTask)
const updatedTaskArray = currentTaskArrayInLs
return updatedTaskArray
}
//save data to local storage
function saveToLs(task) {
const arrayWithTheNewTask = addNewTaskToLsArray(task)
localStorage.setItem("tasks", JSON.stringify(arrayWithTheNewTask))
}
You showHideTasksDetails function is not re-rendering the page by itself.
You can modify it so that the showTasks function is called again when the showHideTaskDetails is called.
function showHideTaskDetails() {
hideContent = !hideContent;
console.log(hideContent);
if (hideContent) {
color = "'background-color: red'";
} else {
color = "";
}
// const test = document.getElementsByTagName('div')
// test.style = "background-color: red"
showTasks();
}
first it's onclick not onClick
Second initial value of hideContent is set to true and you're changing it to false when you're calling the showHideTaskDetails fn before if statement
Related
I am trying to build todo list and I like to put on button(.cancel-task) action which remove exactly item which connected with that button, but when I try to put addEventListener I meet error like "its not a function". Please explain me how to make it with using attribute id which I add before for tasks and also how to remove this item from local storage. Thank you, for your attention.
const taskList = document.querySelector(".todo_tasks-wrapper");
const formTodo = document.querySelector(".control");
const inputTask = document.querySelector(".todo_input");
const btnDeleteTask = document.querySelectorAll(".cancel-task");
const taskKeeper = [];
let taskIdCounter = 0;
const data = JSON.parse(localStorage.getItem("tasks"));
const updateHtml = (taskObj) => {
const newLi = document.createElement("li");
newLi.innerHTML = `<li id="${taskObj.id}" class="item-task">
<span>${taskObj.task}</span>
<button class="cancel-task">
<img src="assets/todo-cancel.png" alt="Cancel">
</button>
</li>`;
taskList.append(newLi);
}
const newTask = (info) => {
taskIdCounter += 1;
const taskObj = {
task: info,
id: taskIdCounter,
};
taskKeeper.push(taskObj);
localStorage.setItem("tasks", JSON.stringify(taskKeeper));
updateHtml(taskObj);
};
formTodo.addEventListener("submit", event => {
event.preventDefault();
const info = inputTask.value.trim();
if(info.length !== 0) {
newTask(info);
inputTask.value = "";
inputTask.focus();
}
});
if(data !== null) {
for(let item of data) {
updateHtml(item);
}
}
<div class="todo_wrapper">
<ul class="todo_tasks-wrapper">
</ul>
<form class="control" action="">
<label class="todo_label-form" for="task">
<input class="todo_input" id="task" type="text" placeholder="Enter new task" maxlength="30">
<input class="todo_submit" type="submit" value="+">
</label>
</form>
</div>
You can use a onclick listener on the todo_tasks-wrapper element and check every element inside the event path to match the criterias.
Example:
todoTaskWrapper.addEventListener("click", (event) => {
for (let el of event.composedPath()) {
// window and document have no matches function, but they are included in the path
if (el.matches && el.matches("button.cancel-task")) {
console.log(el, "is the button clicked")
console.log(el.parentNode, "is the li element");
// remove it
el.parentNode.remove();
}
}
});
MDN: Event.composedPath()
MDN: Element.matches()
Good day! I've been trying to figure out how to create a delete function in my todo app. I dont know what to do next and the delete function as well as the eventListener is not correct. I hope you guys can help me. I want to fully understand every small projects that I make. Thank you in advance! :)
const inputBox = document.querySelector('.input')
const addBtn = document.querySelector('.input-button')
const todoMain = document.querySelector('.todo-list')
const deleteBtn = document.querySelector('.delete-button')
const deleteAllBtn = document.querySelector('.clear-all')
//Event listeners
inputBox.addEventListener("keyup", function(){
let userInput = inputBox.value;
if (userInput.trim() != 0) {
addBtn.classList.add("active")
} else {
addBtn.classList.remove("active");
}
})
addBtn.addEventListener("click", todoAdd);
todoMain.addEventListener("click", todoDelete);
// Functions
function todoAdd(event){
event.preventDefault();
const todoLi = document.createElement('li');
todoLi.innerText = inputBox.value;
const todoDeleteBtn = document.createElement('button');
todoDeleteBtn.innerHTML = `<i class="fas fa-trash-alt"></i>`;
todoDeleteBtn.classList.add('delete-button')
todoLi.appendChild(todoDeleteBtn);
todoMain.appendChild(todoLi);
inputBox.value = '';
addBtn.classList.remove("active");
};
function todoDelete(e){
const item = e.target;
if (item.classList[0] === 'delete-button'){
todoMain.removeChild(todoLi);
}
}
<link crossorigin rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" />
<div class="container">
<h1>TODO list</h1>
<div class="input-container">
<input type="text" class="input" placeholder="Input Text Here">
<button class="input-button"><i class="fas fa-plus"></i></button>
</div>
<ul class="todo-list">
</ul>
<div class="footer">
<span>You have<span class="pending">0</span>pending task left</span>
<button class="clear-all">Clear All</button>
</div>
</div>
I have implemented it quite simply as an example.
When creating the ToDo item, I add a key for the text and a data attribute for the delete button and an onclick event.
The key is important to have the relation between button and text. First i used new Date() but i updated with an random Method. (Math.random()+1).toString().split('.')[1];
For deletAll() you can get the entire Parent Node and set html to an empty string.
const inputBox = document.querySelector('.input')
const addBtn = document.querySelector('.input-button')
const todoMain = document.querySelector('.todo-list')
const deleteBtn = document.querySelector('.delete-button')
const deleteAllBtn = document.querySelector('.clear-all')
//Event listeners
inputBox.addEventListener("keyup", function(){
let userInput = inputBox.value;
if (userInput.trim() != 0) {
addBtn.classList.add("active")
} else {
addBtn.classList.remove("active");
}
})
addBtn.addEventListener("click", todoAdd);
todoMain.addEventListener("click", todoDelete);
// Functions
function todoAdd(event){
event.preventDefault();
const todoLi = document.createElement('li');
const key =(Math.random()+1).toString().split('.')[1];
todoLi.innerText = inputBox.value;
todoLi.setAttribute("id", key);
const todoDeleteBtn = document.createElement('button');
todoDeleteBtn.innerHTML = `<i class="fas fa-trash-alt"></i>`;
todoDeleteBtn.classList.add('delete-button')
todoDeleteBtn.onclick = function() {
const _key = this.getAttribute('data-key')
document.getElementById(_key).remove();
this.remove()
}
todoDeleteBtn.setAttribute("data-key", key);
todoLi.appendChild(todoDeleteBtn);
todoMain.appendChild(todoLi);
inputBox.value = '';
addBtn.classList.remove("active");
};
function todoDelete(e){
const item = e.target;
if (item.classList[0] === 'delete-button'){
todoMain.removeChild(todoLi);
}
}
<link crossorigin rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" />
<div class="container">
<h1>TODO list</h1>
<div class="input-container">
<input type="text" class="input" placeholder="Input Text Here">
<button class="input-button"><i class="fas fa-plus"></i></button>
</div>
<ul class="todo-list">
</ul>
<div class="footer">
<span>You have<span class="pending">0</span>pending task left</span>
<button class="clear-all">Clear All</button>
</div>
</div>
There are a couple of issues that might be stopping this from working:
Your function todoDelete tries to access the variable todoLi, which doesn't exist in its scope. You define this inside todoAdd, but its scope is limited so you can't access the variable from outside the function.
I suspect what you might want to be doing is passing item instead.
You attach the event listener that triggers todoDelete to your todoMain element, which means that e.target for the function will always be the ul element, not your list element. Your if is then always false so the code never runs.
To fix this, attach the event listener to the todoDeleteBtn in your todoAdd function instead.
I'm quite new to Javascript and JQuery and i can't seem to figure out why the counter increments for one button but the same code doesn't work for the others. Any help would be greatly appreciated. Thanks!
// JAVASCRIPT/JQUERY (to increment by 1)
let addRoom = document.querySelector(".addRoom");
let subtractRoom = document.querySelector(".subtractRoom");
let input = document.querySelector(".roomsAmmount");
addRoom.addEventListener('click', () => {
input.value = parseInt(input.value) + 1;
});
subtractRoom.addEventListener('click', () => {
input.value = parseInt(input.value) - 1;
});
/*.addRoom,.subtractRoom{position:absolute;font-size:1.3em;width:50%;height:45px;z-index:1;display:flex}.roomsAmmount{position:absolute;width:50px;height:45px;right:-60px;border:none;border-bottom:1px solid #f4b494;outline:0;font-size:1.4em;text-align:center;background-color:transparent;color:#fff}
*/
<div class="bedroomsButton">Bedrooms
<div class="addRoom">
<span class="plusOne">+</span>
</div>
<div class="subtractRoom">
<span class="subtractOne">-</span>
</div>
<input class="roomsAmmount" type="number" value="0">
</div>
<div class="bathroomsButton">Bathrooms
<div class="addRoom">
<span class="plusOne">+</span>
</div>
<div class="subtractRoom">
<span class="subtractOne">-</span>
</div>
<input class="roomsAmmount" type="number" value="0">
</div>
Document.querySelector()
The Document method querySelector() returns the first Element within the document that matches the specified selector, or group of selectors
You can target all the elements with querySelectorAll(), then loop through them using forEach() to attach the event handler.
// JAVASCRIPT/JQUERY (to increment by 1)
let addRoom = document.querySelectorAll(".addRoom");
let subtractRoom = document.querySelectorAll(".subtractRoom");
addRoom.forEach((el)=>{
el.addEventListener('click', (e) => {
let input = e.target.closest('.bedroomsButton,.bathroomsButton').querySelector(".roomsAmmount");
input.value = parseInt(input.value) + 1;
});
});
subtractRoom.forEach((el)=>{
el.addEventListener('click', (e) => {
let input = e.target.closest('.bedroomsButton,.bathroomsButton').querySelector(".roomsAmmount");
input.value = parseInt(input.value) - 1;
});
});
<div class="bedroomsButton">Bedrooms
<div class="addRoom">
<span class="plusOne">+</span>
</div>
<div class="subtractRoom">
<span class="subtractOne">-</span>
</div>
<input class="roomsAmmount" type="number" value="0">
</div>
<div class="bathroomsButton">Bathrooms
<div class="addRoom">
<span class="plusOne">+</span>
</div>
<div class="subtractRoom">
<span class="subtractOne">-</span>
</div>
<input class="roomsAmmount" type="number" value="0">
</div>
like the comment says, document.querySelector only returns either first element or null.
Do so:
let addRoom = document.querySelectorAll(".addRoom");
let subtractRoom = document.querySelectorAll(".subtractRoom");
addRoom.forEach(btn => {
setInputValue(btn, true)
}
)
subtractRoom.forEach(btn => {
setInputValue(btn, false)
}
)
function setInputValue(btn, plus) {
const input = btn.parentNode.querySelector('input')
btn.addEventListener('click', () => {
const originalValue = parseInt(input.value)
input.value = String(plus ? originalValue + 1 : originalValue - 1)
});
}
add class clickable to every + and -
select all clickable and add event listener on every element
document.querySelectorAll(`.clickable`).forEach(el => el.addEventListener(`click`, handleClick))
handleClick function look like this
const handleClick = (e) => {
// selecting a closest input
const input = e.target.parentElement.parentElement.querySelector(".roomsAmmount");
// parsing value
let newValue = parseInt(input.value);
// if clicked element has class plusOne
if (e.target.classList.contains(`plusOne`)) {
newValue++; // increase
}
// if clicked element has class subtractOne
if (e.target.classList.contains(`subtractOne`)) {
newValue--; // decrease
}
// update value
input.value = newValue;
}
working example https://jsfiddle.net/yqp5zx1b/1/
This code successfully takes the contents of the form and saves it to an ordered list, 2 more functions do the same thing but instead create a timestamp. I'm trying to take every li element that gets generated and save it to localStorage when you push the save button and then repopulate it again from the local storage when you push the "load" button. I can't get it to work come hell or high water. The load button does nothing, and oddly enough the "save" button acts as a clear all and actually removes everything rather then saving it. Console log shows no errors. I have the JavaScript below and the corresponding HTML.
let item;
let text;
let newItem;
function todoList() {
item = document.getElementById("todoInput").value
text = document.createTextNode(item)
newItem = document.createElement("li")
newItem.onclick = function() {
this.parentNode.removeChild(this);
}
newItem.onmousemove = function() {
this.style.backgroundColor = "orange";
}
newItem.onmouseout = function() {
this.style.backgroundColor = "lightblue";
}
todoInput.onclick = function() {
this.value = ""
}
newItem.appendChild(text)
document.getElementById("todoList").appendChild(newItem)
};
function save() {
const fieldvalue = querySelectorAll('li').value;
localStorage.setItem('item', JSON.stringify(item));
}
function load() {
const storedvalue = JSON.parse(localStorage.getItem(item));
if (storedvalue) {
document.querySelectorAll('li').value = storedvalue;
}
}
<form id="todoForm">
<input id="todoInput" value="" size="15" placeholder="enter task here">
<button id="button" type="button" onClick="todoList()">Add task</button>
<button id="save" onclick="save()">Save</button>
<button id="load" onclick="load()">Load</button>
</form>
As #Phil and #Gary explained part of your problem is trying to use querySelectorAll('li') as if it would return a single value. You have to cycle through the array it returns.
Check the below code to give yourself a starting point. I had to rename some of your functions since they were causing me some errors.
<form id="todoForm">
<input id="todoInput" value="" size="15" placeholder="enter task here">
<button id="button" type="button" onClick="todoList()">Add task</button>
<button id="save" onclick="saveAll()" type="button">Save</button>
<button id="load" onclick="loadAll()" type="button">Load</button>
</form>
<div id="todoList"></div>
<script>
let item;
let text;
let newItem;
function todoList() {
item = document.getElementById("todoInput").value
text = document.createTextNode(item)
newItem = document.createElement("li")
newItem.onclick = function() {
this.parentNode.removeChild(this);
}
newItem.onmousemove = function() {
this.style.backgroundColor = "orange";
}
newItem.onmouseout = function() {
this.style.backgroundColor = "lightblue";
}
todoInput.onclick = function() {
this.value = ""
}
newItem.appendChild(text)
//Had to add the element
document.getElementById("todoList").appendChild(newItem);
}
function saveAll() {
//Create an array to store the li values
var toStorage = [];
var values = document.querySelectorAll('li');
//Cycle through the li array
for (var i = 0; i < values.length; i++) {
toStorage.push(values[i].innerHTML);
}
console.log(toStorage);
//CanĀ“t test this on stackoverflow se the jsFiddle link
localStorage.setItem('items', JSON.stringify(toStorage));
console.log(localStorage);
}
function loadAll() {
const storedvalue = JSON.parse(localStorage.getItem('items'));
console.log(storedvalue);
//Load your list here
}
</script>
Check https://jsfiddle.net/nbe18k2u/ to see it working
<p><input type="text" name="adcode" id="cityName"
style="border:none;color:green;"/></p>
<script>
window.onload = function() {
document.getElementById('cityName').value = GetDivElement();
}
</script>
The above code, I am not able to call the "GetDivElement" in paragraph
<p id="cityName"></p>
How to pass the GetElementId in p id??
If you want to get the GetDivElement() method's return value to the paragraph, you can do it like below:
window.onload = function() {
let getDivElement = () => {
return 'Div element data';
};
let inputbox = document.getElementById('cityInput');
let cityParagraph = document.getElementById('cityValue');
inputbox.value = cityParagraph.innerHTML = getDivElement();
}
<div>
<input id="cityInput" />
<p id="cityValue"></p>
</div>
As I understood from your later comment, if you want to get the paragraph value if the id='coimbatore' which you get from the GetDivElement() method you can do the following:
window.onload = function() {
let getDivElement = () => {
return 'coimbatore';
};
let paraId = getDivElement();
let textboxId = `${paraId}CityInput`;
let inputbox = document.getElementById(textboxId);
let cityParagraph = document.getElementById(paraId);
console.log(`Text field value is : ${inputbox.value}`);
console.log(`Paragraph value is : ${cityParagraph.innerHTML}`);
}
<div>
<input id="delhiCityInput" value="delhi input"/>
<p id="delhi">
Delhi city data
</p>
</div>
<div>
<input id="mumbaiCityInput" value="mumbai input"/>
<p id="mumbai">
Mumbai city data
</p>
</div>
<div>
<input id="coimbatoreCityInput" value="coimbatore input"/>
<p id="coimbatore">
Coimbatore city data
</p>
</div>
Couple of things you need to understand here is,
You can't set the same id for multiple html elements
If you want to get the value from a paragraph you should use innerHTML
Hope this helps.