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
I am trying to make a logic that if user input, date, and time are not filled it gives the user an alert to have the mentioned sections above, but for some reason, even when I have those sections containing values it still shows null.
The outcome should be:
If there is no value for user input, a due date or time alert should appear.
// variables object
const el = {
form: document.querySelector(".form"),
input: document.querySelector(".user-input"),
list: document.querySelector(".list"),
date: document.querySelector(".date"),
time: document.querySelector(".time")
};
//local storage key
const storage_key = "tasks-storage-key";
//Create ID
const createId = () => `${Math.floor(Math.random() * 10000)}-${new Date().getTime()}`;
//variable of empty array that gets new task
let taskList = [];
// function that renders task list
//function that creates new tasks with date and time
const creatTask = (task) => {
const id = createId();
const taskNew = el.input.value;
const taskDate = el.date.value;
const taskTime = el.time.value;
const tasks = document.createElement("div");
tasks.innerHTML = `
<div class="task-content">
<div class="list-of-task">
<div class="task" data-id="${id}">
<input type="checkbox" class="tick">
<div class="new-task-created">${taskNew}</div>
<label class="due-date">${taskDate}</label>
<label class="due-time">${taskTime}</label>
</div>
<div class="atcion-buttons">
<button class="edit" data-id="">Edit</button>
<button class="delete" data-id="">Delete</button>
</div>
</div>`;
taskList.push(tasks);
console.log(taskList);
el.list.appendChild(tasks);
return task
};
//event listner that listens for add button.
function addTask(taskNew, taskDate, taskTime) {
if (taskNew == null) {
alert("Please add a new Task")
}
if (taskDate == null) {
alert("Please add a new Task with a due date");
}
if (taskTime == null) {
alert("Please add a new Task with a due time");
}
creatTask();
}
<div class="form">
<input class="user-input" type="text">
<input class="date" type="date">
<input class="time" type="time">
<button onclick="addTask()" class="add" id="add">+</button>
</div>
<div class="list"></div>
All values obtained from form elements are strings. Even if the element is empty, the value is still "", not null so checking for null isn't the right approach.
Instead test for the absence of any "truthy" value as seen below.
// variables object
const el = {
form: document.querySelector(".form"),
input: document.querySelector(".user-input"),
list: document.querySelector(".list"),
date: document.querySelector(".date"),
time: document.querySelector(".time")
};
//local storage key
const storage_key = "tasks-storage-key";
//Create ID
const createId = () => `${Math.floor(Math.random() * 10000)}-${new Date().getTime()}`;
//variable of empty array that gets new task
let taskList = [];
// function that renders task list
//function that creates new tasks with date and time
const creatTask = (task) => {
const id = createId();
const taskNew = el.input.value;
const taskDate = el.date.value;
const taskTime = el.time.value;
const tasks = document.createElement("div");
tasks.innerHTML = `
<div class="task-content">
<div class="list-of-task">
<div class="task" data-id="${id}">
<input type="checkbox" class="tick">
<div class="new-task-created">${taskNew}</div>
<label class="due-date">${taskDate}</label>
<label class="due-time">${taskTime}</label>
</div>
<div class="atcion-buttons">
<button class="edit" data-id="">Edit</button>
<button class="delete" data-id="">Delete</button>
</div>
</div>`;
taskList.push(tasks);
console.log(taskList);
el.list.appendChild(tasks);
return task
};
//event listner that listens for add button.
function addTask(taskNew, taskDate, taskTime) {
if (!taskNew) {
alert("Please add a new Task")
}
if (!taskDate) {
alert("Please add a new Task with a due date");
}
if (!taskTime) {
alert("Please add a new Task with a due time");
}
creatTask();
}
<div class="form">
<input class="user-input" type="text">
<input class="date" type="date">
<input class="time" type="time">
<button onclick="addTask()" class="add" id="add">+</button>
</div>
<div class="list"></div>
Thanks for the help.
I also found another way of doing it as well after doing further research.
// variables object
const el = {
form: document.querySelector(".form"),
input: document.querySelector(".user-input"),
list: document.querySelector(".list"),
date: document.querySelector(".date"),
time: document.querySelector(".time")
};
//local storage key
const storage_key = "tasks-storage-key";
//Create ID
const createId = () => `${Math.floor(Math.random() * 10000)}-${new Date().getTime()}`;
//variable of empty array that gets new task
let taskList = [];
// function that renders task list
//function that creates new tasks with date and time
const creatTask = (task) => {
const id = createId();
const taskNew = el.input.value;
const taskDate = el.date.value;
const taskTime = el.time.value;
if (taskNew.length == 0) {
alert("Please add a new Task");
}
if (taskDate.length == 0) {
alert("Please add a new Task with a due date");
}
if (taskTime.length == 0) {
alert("Please add a new Task with a due time");
}
const tasks = document.createElement("div");
tasks.innerHTML = `
<div class="task-content">
<div class="list-of-task">
<div class="task" data-id="${id}">
<input type="checkbox" class="tick">
<div class="new-task-created">${taskNew}</div>
<label class="due-date">${taskDate}</label>
<label class="due-time">${taskTime}</label>
</div>
<div class="atcion-buttons">
<button class="edit" data-id="">Edit</button>
<button class="delete" data-id="">Delete</button>
</div>
</div>`;
taskList.push(tasks);
console.log(taskList);
el.list.appendChild(tasks);
return task
};
//event listner that listens for add button.
function addTask() {
creatTask();
}
<div class="form">
<input class="user-input" type="text">
<input class="date" type="date">
<input class="time" type="time">
<button onclick="addTask()" class="add" id="add">+</button>
</div>
<div class="list"></div>
I am very new to javascript I have been working on a to-do app for the last couple of days, for a coding Bootcamp project and I have updated my coding format so that it is easier to read than what I had before but now the edit and delete functions are not working.
I set up the id and elements for the tasks.
const createTask = () => {
const id = createId()
const task = elements.input.value;
const date = elements.cal.value;
if(!task && !date) return alert("Please fill in task and select date");
if(!task) return alert("Please fill in task");
if(!date) return alert("Please select date");
const tasks = document.createElement("div");
tasks.innerHTML = `
<button class = "sort" data-id="${id}">Sort</button>
<div class="task" date-id = "${id}">
<div class="content">
<input type ="checkbox" class="tick">
<input type ="text" class = text id = "text" data-id="" readonly>${task}
<label class = "due-date" for ="text">${date}</label>
<input type ="date" class = date id = "date">
</div>
<div class = "actions">
<button class="edit" data-id="${id}">Edit</button>
<button class="delete" data-id="${id}">Delet</button>
</div>
</div>
`
elements.list.appendChild(tasks)
return tasks
}
I set up the event listener for the submit, edit and delete. The add list button works fine but the edit and delete button are not working.
elements.list.addEventListener('click',event => {
const {target} = event;
const {id} = target.dataset
const task = id ? document.querySelector('[data-id="${id}"]'): null
const type = {
edit: event.target.classList.contains('edit'),
delete: event.target.classList.contains('delete'),
}
const isFromSaveLabel = target.innerText.toLowerCase() === 'save'
if(tasks && type.edit && isFromSaveLabel){
const text = task.querySelector('text')
target.innerText = 'Edit'
text.addAttribute('readonly')
return
}
if(tasks && type.edit){
const text = task.querySelector('text')
target.innerText = 'Save'
text.removeAttribute('readonly')
text.focus()
return
}
if(tasks && type.delete){
return
}
});
const submitHandler = (event) =>{
event.preventDefault();
createTask();
}
elements.form.addEventListener("submit", submitHandler);
Is it possible for someone to help me to have the following implemented in my code that does the following:
Sort the list alphabetically
can edit the calendar as well when you edit the list you want to change
save the user list using local storage.
It is just those 3 things that need to be added. I code almost every single day for hours and javascript is a bit challenging for me and I do struggle sometimes and this project is due on Friday.
I'm running into an issue with my current JS project. It's a simple library where the user inputs the info and spits it out onto the page. I have a delete button that I add to each new div, which I've added event listeners to the buttons. When I click delete, it will delete the first one - but that's it. It stops working if i want to delete more than one book.
I think my problem is how I'm targeting the data-set/index values that i assign each div, but I'm not sure. I've tried for loops, for each, etc and can't seem to figure it out.
Any help would be appreciated.
const book1 = new Book('inserttitlehere', 'His name?', 63, false)
const book2 = new Book('kill bill', 'author2', 653, false)
const book3 = new Book('oh yeah baby', 'author3', 323, false)
const book4 = new Book('kill me now', 'author4', 132, true)
library.push(book1, book2, book3, book4)
// Book constructor
function Book(title, author, pages, completed) {
this.title = title
this.author = author
this.pages = pages
this.completed = completed
}
const main = document.querySelector('main');
const form = document.querySelector('.form');
//Function to add books to the DOM
function displayBooks() {
let dataIndex = 0;
main.innerHTML = '';
library.forEach(book => {
const bookElement = document.createElement('div')
bookElement.classList.add('book');
bookElement.setAttribute('data-index', dataIndex);
bookElement.innerHTML = `
<h3> ${book.title} </h3>
<p> Author: ${book.author}</p>
<p> Pages: ${book.pages}</p>
Completed: <input type="checkbox"> ${book.completed} <br>
<button class="delete">Delete</button>
`
main.appendChild(bookElement);
dataIndex++;
})
}
displayBooks();
//Add new book to library
function addBookToLibrary(title, author, pages, completed) {
const newBook = new Book(title, author, pages, completed)
library.push(newBook);
}
//Deleting a book from the array
let deleteBtns = document.querySelectorAll('.book .delete');
deleteBtns.forEach(button => {
button.addEventListener('click', () => {
const index = button.parentNode.dataset['data-index'];
deleteBook(index);
})
});
function deleteBook(index) {
library.splice(index, 1);
displayBooks();
}
form.addEventListener('submit', (e) => {
e.preventDefault();
const title = document.querySelector('#title').value;
const author = document.querySelector('#author').value;
const pages = document.querySelector('#pages').value;
addBookToLibrary(title, author, pages, false);
document.forms[0].reset();
})
console.log(library);
console.log(deleteBtns);
!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="style.css">
<title>Library</title>
</head>
<body>
<nav>
<h1>Library</h1>
<button class="add-book-btn">NEW BOOK</button>
</nav>
<main></main>
<div class="modal">
<form class="form">
<p class="close"></p>
<h2>Add Book</h2>
<div class="user-input">
<label for="">Title</label><br>
<input type="text" id="title" required>
</div>
<div class="user-input">
<label for="">Author</label><br>
<input type="text" id="author" required>
</div>
<div class="user-input">
<label for="">Pages</label><br>
<input type="number" id="pages" required>
</div>
<div class="user-input">
<label for="">Read?</label>
<input type="checkbox" id="read">
</div>
<button type="submit" id="submit">Submit</button>
</form>
</div>
<script src="script.js"></script>
</body>
</html>
When you are building an app like this it's often best to remove the parts that aren't relevant such as the form and all it;s associated functions in order to work on specific parts of it like these user interactions.
Here's a scaled down version with a completely different approach that adds event listeners to the book elements individually as you create them.
Then instead of worrying about indexing, use array methods to find the book object in the library. So rather than rebuild all the elements when you remove one you simply remove both the element and the object in the array.
It's broken down into smaller functions like addBookEvents() then within each different event handler uses either Array.prototype.find() or Array.prototype.findIndex() to modify library.
Your approach of rebuilding all the elements just to change the indexing is not very scalable or efficient. In order to create the library array used here I just used your library.push(book1,book2...) and then ran console.log(JSON.stringify(library)) and pasted it into this code to keep it lean for this stage.
Note that I changed data-index to data-title on the elements with assumption that titles will be unique in the array. That then allows searching array to find the specific book object. Using a unique book id is more reliable in case of title duplications
const main = document.getElementById('books-list')
library.forEach(createBookElement)
function handleDeleteClick(event) {
const bookElem = event.target.closest('.book');
const title = bookElem.dataset.title;
bookElem.remove();
removeFromLibrary(title)
console.log(title)
}
function handleCompleteChange(event){
const bookElem = event.target.closest('.book');
const title = bookElem.dataset.title;
const checked = event.target.checked;
toggleLibraryComplete(title, checked)
}
function removeFromLibrary(title) {
const idx = library.findIndex(book => book.title === title);
if (idx > -1) {
library.splice(idx, 1);
console.log('library length =', library.length)
}
}
function toggleLibraryComplete(title, checked){
const book = library.find(book => book.title === title);
book.completed = checked;
console.log(JSON.stringify(book))
}
function addBookEvents(bookElement){
const button = bookElement.querySelector('.delete');
button.addEventListener('click', handleDeleteClick);
const checkbox = bookElement.querySelector('.book-complete');
checkbox.addEventListener('change', handleCompleteChange);
}
function createBookElement(book) {
const bookElement = document.createElement('div')
bookElement.classList.add('book');
bookElement.setAttribute('data-title', book.title);
bookElement.innerHTML = `
<h3> ${book.title} </h3>
<p> Author: ${book.author}</p>
<p> Pages: ${book.pages}</p>
Completed: <input class="book-complete" type="checkbox" ${book.completed && 'checked'}> ${book.completed} <br>
<button class="delete">Delete</button>
`;
// add the event listeners for delete and completed
addBookEvents(bookElement);
main.appendChild(bookElement);
}
//console.log(JSON.stringify(library))
<main id="books-list"></main>
<script>
const library =
[{"title":"inserttitlehere","author":"His name?","pages":63,"completed":true},{"title":"kill bill","author":"author2","pages":653,"completed":false},{"title":"oh yeah baby","author":"author3","pages":323,"completed":false},{"title":"kill me now","author":"author4","pages":132,"completed":true}];
</script>
Try this:
var library = []
const book1 = new Book('inserttitlehere', 'His name?', 63, false)
const book2 = new Book('kill bill', 'author2', 653, false)
const book3 = new Book('oh yeah baby', 'author3', 323, false)
const book4 = new Book('kill me now', 'author4', 132, true)
library.push(book1, book2, book3, book4)
// Book constructor
function Book(title, author, pages, completed) {
this.title = title
this.author = author
this.pages = pages
this.completed = completed
}
const main = document.querySelector('main');
const form = document.querySelector('.form');
//Function to add books to the DOM
function displayBooks() {
let dataIndex = 0;
main.innerHTML = '';
library.forEach(book => {
const bookElement = document.createElement('div')
bookElement.classList.add('book');
bookElement.setAttribute('data-index', dataIndex);
bookElement.innerHTML = `
<h3> ${book.title} </h3>
<p> Author: ${book.author}</p>
<p> Pages: ${book.pages}</p>
Completed: <input type="checkbox"> ${book.completed} <br>
<button class="delete">Delete</button>
`
main.appendChild(bookElement);
dataIndex++;
})
}
displayBooks();
//Add new book to library
function addBookToLibrary(title, author, pages, completed) {
const newBook = new Book(title, author, pages, completed)
library.push(newBook);
displayBooks();
}
function addListener() {
//Deleting a book from the array
let deleteBtns = document.querySelectorAll('.book .delete');
deleteBtns.forEach(button => {
button.addEventListener('click', () => {
const index = button.parentNode.dataset['data-index'];
deleteBook(index);
})
});
}
addListener();
function deleteBook(index) {
library.splice(index, 1);
displayBooks();
addListener();
}
form.addEventListener('submit', (e) => {
e.preventDefault();
const title = document.querySelector('#title').value;
const author = document.querySelector('#author').value;
const pages = document.querySelector('#pages').value;
addBookToLibrary(title, author, pages, false);
document.forms[0].reset();
})
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="style.css">
<title>Library</title>
</head>
<body>
<nav>
<h1>Library</h1>
<button class="add-book-btn">NEW BOOK</button>
</nav>
<main></main>
<div class="modal">
<form class="form">
<p class="close"></p>
<h2>Add Book</h2>
<div class="user-input">
<label for="">Title</label><br>
<input type="text" id="title" required>
</div>
<div class="user-input">
<label for="">Author</label><br>
<input type="text" id="author" required>
</div>
<div class="user-input">
<label for="">Pages</label><br>
<input type="number" id="pages" required>
</div>
<div class="user-input">
<label for="">Read?</label>
<input type="checkbox" id="read">
</div>
<button type="submit" id="submit">Submit</button>
</form>
</div>
<script src="script.js"></script>
</body>
</html>
First off, library was not defined so I added an array library at the top. Then, I addressed the problem.
The problem was that every time you delete a book, deleteBtn and the button event listeners were being altered, because the buttons they reference were being deleted. So, just reinitialize them every time you delete a book. I did this by wrapping it in a function and recalling it every time it is needed.
Note that I also added an extra line of displayBooks under addBookToLibrary, to make sure that the page is "refreshed" when a book is added.
I am making a to do list, and to do items are being lost (as expected) when page is refreshed. How do I stop it from clearing the items when the page is refreshed???????
Html:
<div class="row">
<input id="userInput" type="text" placeholder="New item..." maxlength="190"autofocus>
<button id="enter">Add</button>
</div>
<div class="row" id="items">
<div class="listItems col-12">
<ul class="col-12 offset-0 col-sm-8 offset-sm-2">
</ul>
</div>
</div>
</div>
JS:
function createListElement() {
var li = document.createElement("li");
li.appendChild(document.createTextNode(input.value));
ul.appendChild(li);
function crossOut() {
li.classList.toggle("done");
}
li.addEventListener("click",crossOut);
var dBtn = document.createElement("button");
dBtn.appendChild(document.createTextNode("X"));
li.appendChild(dBtn);
dBtn.addEventListener("click", deleteListItem);
function deleteListItem(){
li.classList.add("delete");
}
}
//enter works
function addListAfterKeypress(event) {
if (inputLength() > 0 && event.which ===13) {
createListElement();
}
}
input.addEventListener("keypress", addListAfterKeypress);
You can't get your tasks if you refreshed the page without storing them somewhere,
so I recommend you to use local storage as a starting for you
the whole idea here is when you press enter to submit a task you need to make an array to push each task and at the same time update your local storage with that array
and then you need to make an event listener ('DOMContentLoaded') so if you refresh your page then you retrieve tasks from local storage and append them to dom
I hope this answer clarify your issue
// select elements
const input = document.getElementById('userInput');
const ul = document.querySelector('ul');
function createListElement() {
var li = document.createElement("li");
li.appendChild(document.createTextNode(input.value));
ul.appendChild(li);
// update array with storage
let arr = getTasks();
// push li text to array
arr.push(li.textContent);
// update localStorage
localStorage.setItem('tasks', JSON.stringify(arr));
function crossOut() {
li.classList.toggle("done");
}
li.addEventListener("click", crossOut);
var dBtn = document.createElement("button");
dBtn.appendChild(document.createTextNode("X"));
li.appendChild(dBtn);
dBtn.addEventListener("click", deleteListItem);
function deleteListItem() {
li.classList.add("delete");
}
}
//enter works
function addListAfterKeypress(event) {
if (input.value.length > 0 && event.which === 13) {
createListElement();
// wipe out input
input.value = '';
}
}
input.addEventListener("keypress", addListAfterKeypress);
// check localStorage
function getTasks() {
let tasks;
if (localStorage.getItem('tasks') === null) {
tasks = [];
} else {
tasks = JSON.parse(localStorage.getItem('tasks'));
}
return tasks;
}
// on dom loading append tasks
window.addEventListener('DOMContentLoaded', () => {
// get tasks from storage
let arr = getTasks();
// loop through tasks
let foo = '';
arr.forEach(item => {
foo += `
<li>${item}<button>X</button></li>
`;
});
// append tasks to dom
ul.innerHTML = foo;
});
<div class="row">
<input id="userInput" type="text" placeholder="New item..." maxlength="190" autofocus>
<button id="enter">Add</button>
</div>
<div class="row" id="items">
<div class="listItems col-12">
<ul class="col-12 offset-0 col-sm-8 offset-sm-2">
</ul>
</div>
</div>
</div>
It depends on the use case, if it's something like managing date centrally you need to go with database. if it is some minor data user specific you can go with local storage.
To use localStorage in your web applications, there are five methods to choose from:
setItem(): Add key and value to localStorage
getItem(): Retrieve a value by the key from localStorage
removeItem(): Remove an item by key from localStorage
clear(): Clear all localStorage
key(): Passed a number to retrieve nth key of a localStorage
setItem()
window.localStorage.setItem('name', 'Obaseki Nosa');
const person = {
name: "Obaseki Nosa",
location: "Lagos",
}
window.localStorage.setItem('user', JSON.stringify(person));
getItem()
window.localStorage.getItem('user');
JSON.parse(window.localStorage.getItem('user'));
removeItem()
window.localStorage.removeItem('name');
clear()
window.localStorage.clear();
Note: localStorage is synchronous, with no data protection and limited to 5MB across all major browsers