i am doing the library project from "odin project" website and i am having trouble completing it. my idea is to access the cards particular index in the "library" array of objects, but i am having trouble doing so. my idea is to have a function that creates some type of id from its place in the array ( such as its index ) and use that as access for my delete button. any suggestions? i appreciate your time here is my codepen link
//constructor to add a book to
function Book(title, author, pages) {
this.title = title;
this.author = author;
this.pages = pages;
}
//array of books
const library = [];
//hides and unhides forms
const hide = () => {
var form = document.querySelector("#hide");
if (form.style.display === "none") {
form.style.cssText =
"display: block; display: flex; justify-content: center; margin-bottom: 150px";
} else {
form.style.display = "none";
}
};
//creates form, takes input,creates card, resets and runs hide function when done
const addBookCard = () => {
const bookName = document.querySelector('input[name="bookName"]').value;
const authorName = document.querySelector('input[name="authorName"]').value;
const numPages = document.querySelector('input[name="numPages"]').value;
library.push(new Book(bookName, authorName, numPages));
//just stating variables used within my function
const container = document.querySelector(".flex-row");
const createCard = document.createElement("div");
const divTitle = document.createElement("p");
const divAuthor = document.createElement("p");
const divPages = document.createElement("p");
const deleteBtn = document.createElement("button");
//using a class from my css file
createCard.classList.add("card");
createCard.setAttribute("id","id_num")
deleteBtn.setAttribute("onclick", "remove()")
deleteBtn.setAttribute('id','delBtn')
//geting all info from library
divTitle.textContent = "Title: " + bookName
divAuthor.textContent = "Author: " + authorName
divPages.textContent = "Number of Pages: " + numPages
deleteBtn.textContent = "Delete This Book";
//adding it all to my html
container.appendChild(createCard);
createCard.appendChild(divTitle);
createCard.appendChild(divAuthor);
createCard.appendChild(divPages);
createCard.appendChild(deleteBtn);
document.getElementById("formReset").reset();
hide()
return false
};
var btn = document.querySelector('#newCard');
btn.onclick = addBookCard;
You can change library declaration from const to let.
Then you can push books together with their corresponding deleteBtn, that way you will be able to easily remove an entry that corresponds to the clicked deleteBtn
library.push([new Book(bookName, authorName, numPages), deleteBtn]);
And then you can add event listener on deleteBtn like this
deleteBtn.addEventListener('click', event => {
event.target.parentNode.remove();
library = library.filter(v => v[1] !== event.target);
});
Where the first line removes the element from the DOM, and the second line creates new library array without the removed entry.
function Book(title, author, pages) {
this.title = title;
this.author = author;
this.pages = pages;
}
//array of books
let library = [];
//hides and unhides forms
const hide = () => {
var form = document.querySelector("#hide");
if (form.style.display === "none") {
form.style.cssText =
"display: block; display: flex; justify-content: center; margin-bottom: 150px";
} else {
form.style.display = "none";
}
};
//creates form, takes input,creates card, resets and runs hide function when done
const addBookCard = () => {
const bookName = document.querySelector('input[name="bookName"]').value;
const authorName = document.querySelector('input[name="authorName"]').value;
const numPages = document.querySelector('input[name="numPages"]').value;
//just stating variables used within my function
const container = document.querySelector(".flex-row");
const createCard = document.createElement("div");
const divTitle = document.createElement("p");
const divAuthor = document.createElement("p");
const divPages = document.createElement("p");
const deleteBtn = document.createElement("button");
library.push([new Book(bookName, authorName, numPages), deleteBtn]);
deleteBtn.addEventListener('click', event => {
event.target.parentNode.remove();
library = library.filter(v => v[1] !== event.target);
});
//using a class from my css file
createCard.classList.add("card");
createCard.setAttribute("id","id_num")
deleteBtn.setAttribute('id','delBtn')
//geting all info from library
divTitle.textContent = "Title: " + bookName
divAuthor.textContent = "Author: " + authorName
divPages.textContent = "Number of Pages: " + numPages
deleteBtn.textContent = "Delete This Book";
//adding it all to my html
container.appendChild(createCard);
createCard.appendChild(divTitle);
createCard.appendChild(divAuthor);
createCard.appendChild(divPages);
createCard.appendChild(deleteBtn);
document.getElementById("formReset").reset();
hide()
return false
};
var btn = document.querySelector('#newCard');
btn.onclick = addBookCard;
function hello (){
for (var i = 0; i < library.length ;i++) {
console.log(library[i]);
}
}
body {
margin: 0 auto;
width: 960px;
//background: cyan;
}
.flex-row {
display: flex;
flex-wrap: wrap;
}
.flex-column {
display: flex;
flex-direction: column;
}
.flex-row-form {
display: flex;
justify-content: center;
}
.flex-column-form {
display: flex;
flex-direction: column;
background: purple;
width: 45%;
padding: 20px;
border-radius: 5px;
border: 2px solid black;
color: white;
font-weight: 300;
font-size: 24px;
}
.card {
width: 33.33%;
text-align: center;
height: 200px;
border: 1px solid black;
padding: 20px;
margin: 10px;
border-radius: 10px;
}
.text {
padding-bottom: 20px;
font-weight: 300;
font-size: 20px;
}
p {
font-size: 20px;
font-weight: 400;
}
#newBook {
margin: 30px;
padding: 10px 20px;
cursor: pointer;
font-size: 16px;
color: #dff;
border-radius: 5px;
background: black;
}
#delBtn{
padding:10px;
border-radius:5px;
background:red;
color:white;
font-size:14px;
cursor: pointer;
}
<div id="display"></div>
<button id="newBook" onclick="hide()">New Book</button>
<div class="flex-row-form" id="hide" style= "display:none">
<form class="flex-column-form" id="formReset">
Book Name: <input type="text" name="bookName" value="Book Name" id="title"><br>
Author Name: <input type="text" name="authorName" value="Author Name " id="author"<br>
Number of Pages: <input type="text" name="numPages" value="# of Pages" id="pages" ><br>
<button id="newCard"> Add Book to Library</button>
</form>
</div>
<div class="flex-row">
</div>
And I have removed this line
deleteBtn.setAttribute("onclick", "remove()")
you don't need it anymore since I have added event listener for that button, and it was throwing an error because you didn't define remove function in your code.
Related
I have to create a basic Movie listing site wherein i can add or remove a movie from favorites. Instead of the add to favourite button , I want to add the heart icon from bootstrap. Also the rating of the film (star icons) under each film name.
// DUMMY names
var names = {
1: {
name: "Red Notice",
desc: "An Interpol agent tracks the world's most wanted art thief.",
img: "rn.jpg",
},
2: {
name: "Dune",
desc: "the son of a noble family entrusted with the protection of the most valuable asset and most vital element in the galaxy.",
img: "dune.jpg",
},
3: {
name: " Escape Room",
desc: "Six people unwillingly find themselves locked in another series of escape rooms, slowly uncovering what they have in common to survive..",
img: "esc.jpg",
},
4: {
name: "Antim: The Final Truth",
desc: "The cop played by Salman fights the land mafia in the film. ",
img: "4.jpg",
},
5: {
name: "Dhamaka",
desc: "Reassigned from TV to radio, a frustrated anchor sees both danger and opportunity when he receives threatening calls on the air. ",
img: "5.jpg",
}
};
var movie = {
// (A) PROPERTIES
hPdt: null, // HTML names list
hItems: null, // HTML current movie
items: {}, // Current items in movie
// (B) LOCALSTORAGE movie
// (B1) SAVE CURRENT movie INTO LOCALSTORAGE
save: function() {
localStorage.setItem("movie", JSON.stringify(movie.items));
},
// (B2) LOAD movie FROM LOCALSTORAGE
load: function() {
movie.items = localStorage.getItem("movie");
if (movie.items == null) { movie.items = {}; } else { movie.items = JSON.parse(movie.items); }
},
// (B3) EMPTY ENTIRE movie
nuke: function() {
if (confirm("Empty favourite List?")) {
movie.items = {};
localStorage.removeItem("movie");
movie.list();
}
},
// (C) INITIALIZE
init: function() {
// (C1) GET HTML ELEMENTS
movie.hPdt = document.getElementById("movie-names");
movie.hItems = document.getElementById("movie-items");
// (C2) DRAW names LIST
movie.hPdt.innerHTML = "";
let p, item, part;
for (let id in names) {
// WRAPPER
p = names[id];
item = document.createElement("div");
item.className = "p-item";
movie.hPdt.appendChild(item);
// PRODUCT IMAGE
part = document.createElement("img");
part.src = "img/" + p.img;
part.className = "p-img";
item.appendChild(part);
// PRODUCT NAME
part = document.createElement("div");
part.innerHTML = p.name;
part.className = "p-name";
item.appendChild(part);
// PRODUCT DESCRIPTION
part = document.createElement("div");
part.innerHTML = p.desc;
part.className = "p-desc";
item.appendChild(part);
// ADD TO fav
part = document.createElement("input");
part.type = "button";
part.value = "Add to Favorites";
part.className = "movie p-add";
part.onclick = movie.add;
part.dataset.id = id;
item.appendChild(part);
}
// (C3) LOAD movie FROM PREVIOUS SESSION
movie.load();
// (C4) LIST CURRENT movie
movie.list();
},
// (D) LIST CURRENT movie ITEMS (IN HTML)
list: function() {
// (D1) RESET
movie.hItems.innerHTML = "";
let item, part, pdt;
let empty = true;
for (let key in movie.items) {
if (movie.items.hasOwnProperty(key)) { empty = false; break; }
}
// (D2) movie IS EMPTY
if (empty) {
item = document.createElement("div");
item.innerHTML = "List is empty";
movie.hItems.appendChild(item);
} else {
let p, total = 0,
subtotal = 0;
for (let id in movie.items) {
// ITEM
p = names[id];
item = document.createElement("div");
item.className = "c-item";
movie.hItems.appendChild(item);
// NAME
part = document.createElement("div");
part.innerHTML = p.name;
part.className = "c-name";
item.appendChild(part);
// REMOVE
part = document.createElement("input");
part.type = "button";
part.value = "X";
part.dataset.id = id;
part.className = "c-del movie";
part.addEventListener("click", movie.remove);
item.appendChild(part);
}
// EMPTY BUTTONS
item = document.createElement("input");
item.type = "button";
item.value = "Remove all from Favorites";
item.addEventListener("click", movie.nuke);
item.className = "c-empty movie";
movie.hItems.appendChild(item);
}
},
// (E) ADD ITEM INTO movie
add: function() {
if (movie.items[this.dataset.id] == undefined) {
movie.items[this.dataset.id] = 1;
} else {
movie.items[this.dataset.id]++;
}
movie.save();
movie.list();
},
// (G) REMOVE ITEM FROM movie
remove: function() {
delete movie.items[this.dataset.id];
movie.save();
movie.list();
},
};
window.addEventListener("DOMContentLoaded", movie.init);
body {
background-color: rgb(210, 241, 223);
}
.title {
text-align: center;
font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;
padding-bottom: 20px;
}
#movie-wrap {
font-family: arial, sans-serif;
display: grid;
grid-template-columns: 80% 20%;
margin: 0 auto;
max-width: 4000px;
}
input.movie,
button.movie {
font-weight: bold;
font-size: 1em;
padding: 10px;
border: none;
color: rgb(58, 56, 158);
background: #929cf5;
cursor: pointer;
}
.name {
text-align: center;
}
/* (B) names LIST */
#movie-names {
display: grid;
grid-template-columns: auto auto;
grid-gap: 30px;
padding: 10px;
}
.p-item {
padding: 10px;
border: 1px solid #aaa;
text-align: center;
}
.p-name {
text-transform: uppercase;
font-weight: bold;
font-size: 1.1em;
}
.p-img {
max-width: 180px;
}
.p-desc {
color: #777;
font-size: 0.9em;
line-height: 1.5em;
}
input.p-add {
width: 80%;
}
/* (D) CURRENT SHOPPING movie */
#movie-items {
padding: 10px;
background: #d8cbcb;
margin: 10px;
}
.c-item {
display: flex;
flex-wrap: wrap;
margin-bottom: 10px;
}
.c-name {
width: 80%;
font-size: 1.3em;
line-height: 1.5em;
}
.c-del {
width: 20%;
}
input.c-empty {
width: 100%;
margin-top: 10px;
}
/* (E) RESPONSIVE */
#media (max-width: 768px) {
#movie-wrap {
grid-template-columns: 60% 40%;
}
#movie-names {
grid-template-columns: auto;
}
}
<!DOCTYPE html>
<html>
<head>
<title>MOVIE LISTING WEBSITE</title>
<link rel="stylesheet" href="style.css">
<script src="script.js"></script>
</head>
<body>
<h1 class="title">Movie Listing App</h1>
<div id="movie-wrap">
<div class="name">
<h2>Movie Names</h2>
<div id="movie-names">
</div>
</div>
<div class="name">
<h2>Favorites</h2>
<div id="movie-items">
</div>
</div>
</div>
</body>
</html>
Also I would like to know if there is a less complicated method to do make this site. Any help would be appreciated.
ps: I dont know why the code is showing error here. It runs properly on my pc
see: https://www.tutorialrepublic.com/twitter-bootstrap-tutorial/bootstrap-icons.php
you first need to import bootstrap to your header in your html page:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.2/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons#1.5.0/font/bootstrap-icons.css">
and then you can use icons like this:
<span class="bi-star"></span>
Hello I'm building a todo app with javascript. and i started to make divs and buttons from creating with document.createElement but when i create buttons to remove lists only one button which is written in html is clickable and remove div ,other buttons that created with javascript not clickable,please can anyone tell me how to fix
let menu = document.querySelector(".bs");
let btn1 = document.querySelector(".btn");
let btn2 = document.querySelector(".btn3");
let inp = document.querySelector(".input");
let bsd = document.querySelector(".sss");
let brs = document.querySelector(".marker");
btn1.addEventListener("click", function(){
let br = document.createElement("DIV");
br.className = "red";
br.innerHTML = inp.value;
menu.appendChild(br);
let ttt = document.createElement("BUTTON");
ttt.className = "marker";
ttt.innerHTML = "Remove";
br.appendChild(ttt);
});
brs.addEventListener("click", function(){
let bred = document.querySelector(".but");
let drp = document.querySelector(".red");
bred.removeChild(drp);
});
.red {
background-color: dodgerblue;
width: 100%;
min-height: 50px;
display: flex;
align-items: center;
justify-content: space-around;
color: white;
margin: 30px 0;
}
.marker {
background-color:white;
border:none;
padding:10px 20px;
}
<body>
<div class="but">
<div class="red">
<button class="marker">Remove</button>
</div>
<div class="bs"></div>
</div>
<input type="text" class="input">
<button class="btn">Add</button>
<button class="btn3">Remove</button>
</body>
that
You need to add the click listener to "remove" buttons when you create them.
The following does this:
let menu = document.querySelector(".bs");
let btn1 = document.querySelector(".btn");
let btn2 = document.querySelector(".btn3");
let inp = document.querySelector(".input");
let bsd = document.querySelector(".sss");
let brs = document.querySelector(".marker");
let addBr = () => {
let br = document.createElement("DIV");
br.className = "red";
br.innerHTML = inp.value;
menu.appendChild(br);
let ttt = document.createElement("BUTTON");
ttt.className = "marker";
ttt.innerHTML = "Remove";
br.appendChild(ttt);
// This is the important change. Part of creating the .ttt element
// is setting up its event listeners!
ttt.addEventListener('click', () => br.remove());
};
btn1.addEventListener("click", addBr);
// Call `addBr` once to add the initial element
addBr();
.red {
background-color: dodgerblue;
width: 100%;
min-height: 50px;
display: flex;
align-items: center;
justify-content: space-around;
color: white;
margin: 30px 0;
}
.marker {
background-color:white;
border:none;
padding:10px 20px;
}
<body>
<div class="but">
<div class="bs"></div>
</div>
<input type="text" class="input">
<button class="btn">Add</button>
<button class="btn3">Remove</button>
</body>
I am pretty new to js. I am trying to complete a todo sort of app. I have been able to create, render and delete an array item, but I am having trouble editing.
All these operations are done with the uuidv4 library to generate an id for each array item created.
With an individual id selected for an array item, I am generating dynamic buttons, one for deleting the array item, the other one for editing.
Upon clicking edit, I want to open up a modal that contains the content of the selected array item. After making the changes, the edit button inside the modal should call upon an edit function to update the changes and then rerender the array.
My issue is that I can't open up the modal dialog box when clicking the edit button.
This is the code to create the necessary structure, the code for creating, rendering and deleting is not included as they are working properly.
// Generate the DOM structure for a todo
const generateTodoDOM = function(todo) {
const todoEl = document.createElement("div");
const checkbox = document.createElement("input");
const label = document.createElement("label");
checkbox.appendChild(label);
todoEl.setAttribute("id", "myTodos");
const textEl = document.createElement("p");
const editButton = document.createElement("button");
editButton.setAttribute("id", "modal-btn");
const removeButton = document.createElement("button");
const createDate = document.createElement("p");
createDate.textContent = `Created: ${dateCreated}`;
createDate.style.color = "#956E93";
// Setup the todo text
textEl.textContent = todo.text;
todoEl.appendChild(textEl);
// Setup the remove button
removeButton.textContent = "x";
todoEl.appendChild(removeButton);
removeButton.addEventListener("click", function() {
removeTodo(todo.id);
saveTodos(todos);
renderTodos(todos, filters);
});
// TODO: Setup the edit note button
editButton.textContent = "Edit Todo";
todoEl.appendChild(editButton);
editButton.addEventListener("click", function() {
//Launch the modal
editModal(todo.id);
});
// Setup todo checkbox
checkbox.setAttribute("type", "checkbox");
checkbox.checked = todo.completed;
todoEl.appendChild(checkbox);
checkbox.addEventListener("change", function() {
toggleTodo(todo.id);
saveTodos(todos);
renderTodos(todos, filters);
});
todoEl.appendChild(createDate);
return todoEl;
};
The code for the modal is the following:
//Edit modal todo by id
const editModal = function(id) {
const todoIndex = todos.findIndex(function(todo) {
return todo.id === id;
});
if (todoIndex > -1) {
const modal = document.querySelector("#my-modal");
const modalBtn = document.querySelector("#modal-btn");
const editTodoContentBtn = document.querySelector("#submitEditTodo")
const closeBtn = document.querySelector(".close");
// Events
modalBtn.addEventListener("click", openModal);
closeBtn.addEventListener("click", closeModal);
editTodoContentBtn.addEventListener("click", editTodo)
window.addEventListener("click", outsideClick);
// Open
function openModal() {
modal.style.display = "block";
}
// Close
function closeModal() {
modal.style.display = "none";
}
// Close If Outside Click
function outsideClick(e) {
if (e.target == modal) {
modal.style.display = "none";
}
}
//Edit the content of the textarea
function editTodo(e) {
editTodo(id)
}
}
};
When clicking the submitEditTodo button the following edit function should be fired:
//Edit todo by id
const editTodo = function(id) {
const editTodoContent = document.querySelector('#editTodo')
const todoIndex = todos.findIndex(function(todo) {
return todo.id === id;
});
if (todoIndex > -1) {
editTodoContent.value = todos.text
saveTodos(todos)
renderTodos(todos, filters);
}
};
The saveTodos and renderTodos are functioning properly with other functions for creating, rendering and deleting.
This is the HTML code:
<!-- Edit modal -->
<div id="my-modal" class="modal">
<div class="modal-content">
<div class="modal-header">
<span class="close">×</span>
<h2>Edit Todo</h2>
</div>
<div class="modal-body">
<textarea name="" class="editTextArea" id="editTodo" rows="10"></textarea>
<button class="button" id="submitEditTodo">Edit Todo</button>
</div>
<div class="modal-footer">
<!-- <h3>Modal Footer</h3> -->
</div>
</div>
<!-- End modal -->
and this is the CSS for the modal:
/*
Edit todo modal start
*/
:root {
--modal-duration: 1s;
--modal-color: #BB8AB8;
}
.modal {
display: none;
position: fixed;
z-index: 1;
left: 0;
top: 0;
height: 100%;
width: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.5);
}
.modal-content {
margin: 10% auto;
width: 35%;
box-shadow: 0 5px 8px 0 rgba(0, 0, 0, 0.2), 0 7px 20px 0 rgba(0, 0, 0, 0.17);
animation-name: modalopen;
animation-duration: var(--modal-duration);
}
.editTextArea{
width:100%
}
.modal-header h2,
.modal-footer h3 {
margin: 0;
}
.modal-header {
background: var(--modal-color);
padding: 15px;
color: #fff;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
.modal-body {
padding: 10px 20px;
background: #fff;
}
.modal-footer {
background: var(--modal-color);
padding: 10px;
color: #fff;
text-align: center;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
}
.close {
color: #ccc;
float: right;
font-size: 30px;
color: #fff;
}
.close:hover,
.close:focus {
color: #000;
text-decoration: none;
cursor: pointer;
}
#keyframes modalopen {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
/*
Edit todo modal end
*/
Thanks
Below are a few pointers to where you might need adjustments to achieve what you want.
Currently you are adding new listeners to the modal every time you click an edit button for a todo. This should probably only be set once. Alternatively you should remove the listeners when the modal is closed.
Your function editModal does not actually open the modal. What it does is add a listener to the #modal-btn button which will then open the modal the next time you click the button.
You are setting id's for both the outer div and the edit button, but the id's are not based on anything related to the todo element you are creating. So effectively all those elements end up with the same id. An id (short for identifier) is usually meant to be unique. For grouping of multiple elements you should instead use the class attribute.
Your function "editTodo" calls itself. Recursing indefinitely. Beware of reusing function names.
With that said the below code is a crude way to do what I think you want to do based on the snippets you provided:
// Open
const openModal = function() {
document.querySelector("#my-modal").style.display = "block";
}
// Close
const closeModal = function() {
document.querySelector("#my-modal").style.display = "none";
}
function initModal() {
const modal = document.querySelector("#my-modal");
const closeBtn = document.querySelector(".close");
// Events
closeBtn.addEventListener("click", closeModal);
window.addEventListener("click", outsideClick);
// Close If Outside Click
function outsideClick(e) {
if (e.target == modal) {
modal.style.display = "none";
}
}
}
const filters = []; // dummy variable
// Generate the DOM structure for a todo
var todos = []
function generateTodoDOM(todo) {
todos.push(todo);
const todoEl = document.createElement("div");
const checkbox = document.createElement("input");
const label = document.createElement("label");
checkbox.appendChild(label);
todoEl.setAttribute("id", "my-todos-" + todo.id);
const textEl = document.createElement("p");
const editButton = document.createElement("button");
editButton.setAttribute("id", "modal-btn-" + todo.id);
const removeButton = document.createElement("button");
const createDate = document.createElement("p");
createDate.textContent = 'Created: ' + new Date();
createDate.style.color = "#956E93";
// Setup the todo text
textEl.textContent = todo.text;
todoEl.appendChild(textEl);
// Setup the remove button
removeButton.textContent = "x";
todoEl.appendChild(removeButton);
removeButton.addEventListener("click", function() {
removeTodo(todo.id);
saveTodos(todos);
renderTodos(todos, filters);
});
// TODO: Setup the edit note button
editButton.textContent = "Edit Todo";
todoEl.appendChild(editButton);
editButton.addEventListener("click", function() {
//Launch the modal
editModal(todo.id);
openModal();
});
// Setup todo checkbox
checkbox.setAttribute("type", "checkbox");
checkbox.checked = todo.completed;
todoEl.appendChild(checkbox);
checkbox.addEventListener("change", function() {
toggleTodo(todo.id);
saveTodos(todos);
renderTodos(todos, filters);
});
todoEl.appendChild(createDate);
return todoEl;
};
var editFn
//Edit modal todo by id
const editModal = function(id) {
const todoIndex = todos.findIndex(function(todo) {
return todo.id === id;
});
if (todoIndex > -1) {
const modal = document.querySelector("#my-modal");
const editElm = document.querySelector("#editTodo");
const editTodoContentBtn = document.querySelector("#submitEditTodo")
editElm.value = todos[todoIndex].text;
// Events
editTodoContentBtn.removeEventListener("click", editFn)
//Edit the content of the textarea
editFn = function(e) {
editTodo(id)
closeModal()
}
editTodoContentBtn.addEventListener("click", editFn)
}
};
//Edit todo by id
const editTodo = function(id) {
const editTodoContent = document.querySelector('#editTodo')
const todoIndex = todos.findIndex(function(todo) {
return todo.id === id;
});
if (todoIndex > -1) {
todos[todoIndex].text = editTodoContent.value;
saveTodos(todos)
renderTodos(todos, filters);
}
};
const saveTodos = function(todos) {
// dummy method, we're keeping it in memory for this example
}
const renderTodos = function(todosToRender) {
todos = []; // clear current in-memory array
var todoList = document.getElementById("todo-container");
while (todoList.firstChild) {
todoList.removeChild(todoList.firstChild);
}
for(var i = 0; i < todosToRender.length; i++) {
todoList.appendChild(generateTodoDOM(todosToRender[i]));
}
};
initModal();
const container = document.getElementById("todo-container");
var generatedTodos = [];
for(var i = 0; i < 10; i++) {
var todo = { text: "Todo " + (i+1), id: "todo-" + i, completed: false};
generatedTodos.push(todo);
}
renderTodos(generatedTodos);
/*
Edit todo modal start
*/
:root {
--modal-duration: 1s;
--modal-color: #BB8AB8;
}
.modal {
display: none;
position: fixed;
z-index: 1;
left: 0;
top: 0;
height: 100%;
width: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.5);
}
.modal-content {
margin: 10% auto;
width: 35%;
box-shadow: 0 5px 8px 0 rgba(0, 0, 0, 0.2), 0 7px 20px 0 rgba(0, 0, 0, 0.17);
animation-name: modalopen;
animation-duration: var(--modal-duration);
}
.editTextArea{
width:100%
}
.modal-header h2,
.modal-footer h3 {
margin: 0;
}
.modal-header {
background: var(--modal-color);
padding: 15px;
color: #fff;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
.modal-body {
padding: 10px 20px;
background: #fff;
}
.modal-footer {
background: var(--modal-color);
padding: 10px;
color: #fff;
text-align: center;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
}
.close {
color: #ccc;
float: right;
font-size: 30px;
color: #fff;
}
.close:hover,
.close:focus {
color: #000;
text-decoration: none;
cursor: pointer;
}
#keyframes modalopen {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
/*
Edit todo modal end
*/
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="todo-container">
</div>
<!-- Edit modal -->
<div id="my-modal" class="modal">
<div class="modal-content">
<div class="modal-header">
<span class="close">×</span>
<h2>Edit Todo</h2>
</div>
<div class="modal-body">
<textarea name="" class="editTextArea" id="editTodo" rows="10"></textarea>
<button class="button" id="submitEditTodo">Edit Todo</button>
</div>
<div class="modal-footer">
<!-- <h3>Modal Footer</h3> -->
</div>
</div>
</div>
<!-- End modal -->
</body>
</html>
I'm trying to make a Single Page Application with pure JavaScript (no additional frameworks or libraries). The problem is that the values I add to the TODO list are not storing in the localStorage (and are not showing).
I would appreciate any help with that task.
How can I simplify the code? (without using any additional libraries and frameworks (ex.jquery etc.))
Here is my code:
let inputTask = document.getElementById('toDoEl');
let editTask = document.getElementById('editTask');
let checkTask = document.getElementById('list');
let emptyList = document.getElementById('emptyList');
let items = [];
let id = [];
let labelToEdit = null;
const empty = 0;
let pages = ['index', 'add', 'modify'];
load();
function load() {
items = loadFromLocalStorage();
id = getNextId();
items.forEach(item => renderItem(item));
}
function show(shown) {
location.href = '#' + shown;
pages.forEach(function(page) {
document.getElementById(page).style.display = 'none';
});
document.getElementById(shown).style.display = 'block';
return false;
}
function getNextId() {
for (let i = 0; i<items.length; i++) {
let item = items[i];
if (item.id > id) {
id = item.id;
}
}
id++;
return id;
}
function loadFromLocalStorage() {
let localStorageItems = localStorage.getItem('items');
if (localStorageItems === null) {
return [];
}
return JSON.parse(localStorageItems);
}
function saveToLocalStorage() {
localStorage.setItem('items', JSON.stringify(items));
}
function setChecked(checkbox, isDone) {
if (isDone) {
checkbox.classList.add('checked');
checkbox.src = 'https://image.ibb.co/b1WeN9/done_s.png';
let newPosition = checkTask.childElementCount - 1;
let listItem = checkbox.parentNode;
listItem.classList.add('checked');
checkTask.removeChild(listItem);
checkTask.appendChild(listItem);
} else {
checkbox.classList.remove('checked');
checkbox.src = 'https://image.ibb.co/nqRqUp/todo_s.png';
let listItem = checkbox.parentNode;
listItem.classList.remove('checked');
}
}
function renderItem(item) {
let listItem = document.getElementById('item_template').cloneNode(true);
listItem.style.display = 'block';
listItem.setAttribute('data-id', item.id);
let label = listItem.querySelector('label');
label.innerText = item.description;
let checkbox = listItem.querySelector('input');
checkTask.appendChild(listItem);
setChecked(checkbox, item.isDone);
emptyList.style.display = 'none';
return listItem;
}
function createNewElement(task, isDone) {
let item = { isDone, id: id++, description: task };
items.push(item);
saveToLocalStorage();
renderItem(item);
}
function addTask() {
if (inputTask.value) {
createNewElement(inputTask.value, false);
inputTask.value = '';
show('index');
}
}
function modifyTask() {
if (editTask.value) {
let item = findItem(labelToEdit);
item.description = editTask.value;
labelToEdit.innerText = editTask.value;
saveToLocalStorage();
show('index');
}
}
function findItem(child) {
let listItem = child.parentNode;
let id = listItem.getAttribute('data-id');
id = parseInt(id);
let item = items.find(item => item.id === id);
return item;
}
// Chanhe img to checked
function modifyItem(label) {
labelToEdit = label;
editTask.value = label.innerText;
show('modify');
editTask.focus();
editTask.select();
}
function checkItem(checkbox) {
let item = findItem(checkbox);
if (item === null) {
return;
}
item.isDone = !item.isDone;
saveToLocalStorage();
setChecked(checkbox, item.isDone);
}
function deleteItem(input) {
let listItem = input.parentNode;
let id = listItem.getAttribute('data-id');
id= parseInt(id);
for (let i in items) {
if (items[i].id === id) {
items.splice(i, 1);
break;
}
}
if (items.length === empty) {
emptyList.style.display = 'block';
}
saveToLocalStorage();
listItem.parentNode.removeChild(listItem);
}
* {
box-sizing: border-box;
}
body {
font-family: sans-serif;
}
h2, li, #notification {
text-align: center;
}
h2 {
font-weight: normal;
margin: 0 auto;
padding-top: 20px;
padding-bottom: 20px;
}
#root {
width: 400px;
height: 550px;
margin: 0 auto;
position: relative;
}
#root>ul {
display: block;
}
#addButton {
display: block;
margin: 0 auto;
}
.checkbox, .delete {
height: 24px;
bottom: 0;
}
.checkbox {
float: left;
}
.delete {
float: right;
}
ul {
margin: 20px 30px 0 30px;
padding-top: 20px;
padding-left: 20px;
text-align: center;
}
#toDoEl {
width: 50%;
}
li {
width: 100%;
list-style: none;
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: center;
margin: 15px auto;
}
label {
margin: 0 auto;
text-align: justify;
text-justify: inter-word;
}
label:hover {
cursor: auto;
}
li.checked {
background-color: gray;
}
span.button {
cursor: pointer;
}
#add, #modify {
display: none;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Homework 12 - Simple TODO List</title>
<link rel="stylesheet" href="./assets/styles.css">
</head>
<body>
<div id="root">
<!--Main page-->
<div id="index">
<h2>Simple TODO Application</h2>
<button class="button" id="addButton" onclick="show('add')">Add New Task</button>
<p id="emptyList">TODO is empty</p>
<ul id="list">
<li id="item_template" style="display: none">
<input class="checkbox" type="image" alt="checkbox" src="https://image.ibb.co/nqRqUp/todo_s.png" onclick="checkItem(this)">
<label onclick="modifyItem(this)"></label>
<input id="delete" class="delete" type="image" alt="remove" src="https://image.ibb.co/dpmqUp/remove_s.jpg" onclick="deleteItem(this)">
</li>
</ul>
</div>
<!--Add page-->
<div id="add">
<h2>Add Task</h2>
<input type="text" id="toDoEl">
<button class="button cancel" onclick="show('index')">Cancel</button>
<button class="button save" onclick="addTask()">Save changes</button>
</div>
<!--Modify page-->
<div id="modify">
<h2>Modify item</h2>
<input type="text" id="editTask">
<button class="button cancel" onclick="show('index')">Cancel</button>
<button class="button save" onclick="modifyTask()">Save changes</button>
</div>
</div>
<script src="./src/app.js"></script>
</body>
</html>
Your code does appear to work. If you console.log(JSON.parse(localStorageItems)) right above line 49 in the loadFromLocalStorage function, it shows as expected in the console. Also, upon refreshing the items persist.
If what you mean is that you're checking localStorage and you don't see the items, it might be that you're looking at the preview version of localStorage. (I'm assuming you're using Chrome.) Hover over the top of the empty section and pull down, this should reveal the values stored. If you click on one, it should show in the preview section. I think this was a Chrome dev tools UI change recently implemented.
I checked your code in Codepen and it works.
I have a function that creates an alert box, fills the title field and message with information (error, success, info...) and then adds a button that gets an onclick function to remove the message alert.
When a message is first generated and you click the button nothing happens, but when you click it a second time it'll close the message and works for all messages created later until the page reloads.
var container = document.getElementById('modal-container');
var page = document.getElementById('html');
var current;
function createBox(msgtype) {
var back = document.createElement('span');
back.className = 'modal-window';
container.appendChild(back);
var box = document.createElement('div');
box.className = 'modal-message-box';
back.appendChild(box);
if (msgtype == 'error') {
var head = document.createElement('span');
head.className = 'errorheader';
} else if (msgtype == 'info') {
var head = document.createElement('span');
head.className = 'infoheader';
} else if (msgtype == 'success') {
var head = document.createElement('span');
head.className = 'successheader';
}
box.appendChild(head);
var title = document.createElement('h2');
title.id = 'alert-title';
title.className = 'alert-title';
head.appendChild(title);
var msg = document.createElement('h4');
msg.id = 'alert-message';
msg.className = 'alert-message';
box.appendChild(msg);
var btn = document.createElement('button');
btn.innerHTML = 'Close';
btn.onclick = closemsg;
box.appendChild(btn);
page.className = 'noscroll';
current = document.getElementById('modal-window');
}
function alertBoxPopup(msgtype, title, msg) {
createBox(msgtype);
document.getElementById('alert-title').innerHTML = title;
document.getElementById('alert-message').innerHTML = msg;
}
function closemsg() {
document.getElementById('html').className = '';
container = document.getElementById('modal-container');
container.removeChild(container.firstChild);
}
.modal-container{
display: flex;
align-content: center;
justify-content: center;
}
.modal-window{
background-color: rgba(0,0,0,.5);
height: 100vh;
width: 100vw;
position: fixed;
margin: 0px;
padding: 0px;
z-index: 5;
display: flex;
justify-content: center;
align-items: center;
}
.modal-message-box{
background: white;
max-height: 400px;
min-height: 300px;
max-width: 700px;
min-width: 500;
border-radius: 10px;
overflow: hidden;
}
<html id='html'>
<body>
<div class="modal-container" id="modal-container"></div>
<div class="row"><button onclick="alertBoxPopup('error', 'Uh-Oh!', 'You have screwed up something!')">Error</button> <button onclick="alertBoxPopup('info', 'Information!', 'Did you know? This box is just a bit of information!')">Info</button> <button onclick="alertBoxPopup('success', 'YES!', 'You did it man! You did something good!')">Success</button>
</div>
</body>
</html>
I was able to actually figure this out on accident.
I realized that I stored the page in a variable, was searching for it and on a whim I decided to put the className change after the removeChild. For whatever reason it works.
var container = document.getElementById('modal-container');
var page = document.getElementById('html');
var current;
function createBox(msgtype, extrabtn, ebname, ebfunc){
var back = document.createElement('span');
back.className = 'modal-window';
container.appendChild(back);
var box = document.createElement('div');
box.className = 'modal-message-box';
back.appendChild(box);
if(msgtype == 'error'){
var head = document.createElement('span');
head.className = 'errorheader';
}else if(msgtype == 'info'){
var head = document.createElement('span');
head.className = 'infoheader';
}else if(msgtype == 'success'){
var head = document.createElement('span');
head.className = 'successheader';
}
box.appendChild(head);
var title = document.createElement('h2');
title.id = 'alert-title';
title.className = 'alert-title';
head.appendChild(title);
var msg = document.createElement('h4');
msg.id = 'alert-message';
msg.className = 'alert-message';
box.appendChild(msg);
if(extrabtn){
var row = document.createElement('div');
row.className = 'row';
var xtrbtn = document.createElement('button');
xtrbtn.innerHTML = ebname;
xtrbtn.onclick = ebfunc;
var btn = document.createElement('button');
btn.innerHTML = 'Close';
btn.onclick = function(){
closemsg();
};
box.appendChild(row);
row.appendChild(xtrbtn);
row.appendChild(btn);
}else{
var btn = document.createElement('button');
btn.innerHTML = 'Close';
btn.onclick = function(){
closemsg();
};
box.appendChild(btn);
}
page.className = 'noscroll';
current = document.getElementById('modal-window');
}
function alertBoxPopup(msgtype, title, msg, extrbtn, ebname, ebfunc){
createBox(msgtype, extrbtn, ebname, ebfunc);
document.getElementById('alert-title').innerHTML = title;
document.getElementById('alert-message').innerHTML = msg;
}
function closemsg(){
container = document.getElementById('modal-container');
container.removeChild(container.firstChild);
page.className = '';
}
.modal-container{
display: flex;
align-content: center;
justify-content: center;
}
.modal-window{
background-color: rgba(0,0,0,.5);
height: 100vh;
width: 100vw;
position: fixed;
margin: 0px;
padding: 0px;
z-index: 5;
display: flex;
justify-content: center;
align-items: center;
}
.modal-message-box{
background: white;
max-height: 400px;
min-height: 300px;
max-width: 700px;
min-width: 500;
border-radius: 10px;
overflow: hidden;
}
<html id="html">
<body>
<div class="modal-container" id="modal-container"></div>
<div class="row">
<button onclick="alertBoxPopup('error', 'Uh-Oh!', 'You have screwed up something!')">Error</button>
<button onclick="alertBoxPopup('info', 'Information!', 'Did you know? this is just information!')">Info</button>
<button onclick="alertBoxPopup('success', 'YES!', 'You did it man! You did something good!')">Success</button>
</div>
<body>
<html>