I am currently working on an exercise in HTML/CSS/JS. The premise is a library website. The idea is that I have a collapsable form that when deployed allows the user to enter the title of the book, the author, and page numbers. When submit is clicked it takes the user input and generates a html card on the webpage. This title card will display the user input information along with a slider to denote if it was read or not.
I have a few test cards generated in my code that creates the cards as intended, however when utilizing the html form i can not seem to get additional cards to create. I ran some tests to the console to ensure that the form is in fact capturing the user input and assigning it.
If anyone could point me in the right direction that would be amazing. I have been messing with this issue for almost 2 weeks with no luck. I have a feeling that my "wires" are not connected right in the javascript. Thank you in advance.
Javascript:
// set up library
let myLibrary = [];
//set up object
function Book(title, author, pages) {
this.title = title;
this.author = author;
this.pages = pages;
}
//add object to library
function addBookToLibrary(Book){
myLibrary.push(Book);
}
//test books
const lotr = new Book('The Fellowship of the Ring', 'J.R.R. Tolkein', '423 pages');
const ender = new Book('Enders Game', 'Orson Scott Card', '324 pages');
const martian = new Book('The Martian', 'Anthony Weir', '369 pages');
addBookToLibrary(lotr);
addBookToLibrary(ender);
addBookToLibrary(martian);
//inject html to create cards
let htmlCode = ``;
myLibrary.forEach(function(singleBookObjects){
htmlCode =
htmlCode +
`
<entry>
<div>
<h3>Title: ${singleBookObjects.title}</h3>
<h3>Author: ${singleBookObjects.author}</h3>
<h3>Pages: ${singleBookObjects.pages}</h3>
<label class="switch">
<input type="checkbox">
<span class="slider round"></span>
</label>
</div>
</entry>
`;
});
function newBookCard() {
let newdiv = document.createElement('div');
newdiv.className+= 'item';
let newp = document.createElement('p');
newp.innerHTML = "Title";
newdiv.appendChild(newp);
document.getElementById('main').appendChild(newdiv);
}
const addBtn = document.getElementById("submit");
addBtn.onclick = function() {
let a = document.getElementById("bookTitle").value
let b = document.getElementById("bookAuthor").value
let c = document.getElementById("bookPages").value
let newBook = new Book(a, b, c);
addBookToLibrary(newBook);
console.log(myLibrary);
newBookCard();
document.getElementById("bookTitle").value = ""
document.getElementById("bookAuthor").value = ""
document.getElementById("bookPages").value = ""
}
const bookCards = document.querySelector(".all-book-cards");
bookCards.innerHTML = htmlCode;
function openForm() {
document.getElementById("myForm").style.display = "block";
}
function submitForm(){
let a = document.getElementById("bookTitle")
}
function closeForm() {
document.getElementById("myForm").style.display = "none";
}
HTML:
<!DOCTYPE html>
<html>
<head>
<title>Library</title>
<meta charset="UTF-8"/>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id = "container" img src="images/bookShelf.jpeg">
<div id = "titleCard"> My Library</div>
<div class = "all-book-cards"></div>
<button class="open-button" onclick="openForm()">Add Book</button>
<div class="form-popup" id="myForm">
<form action="index.html" class="form-container">
<h1>Add Book</h1>
<label for="bookTitle"><b>Title:</b></label>
<input id="bookTitle" type="text" placeholder="Enter Title" name="bookTitle" required>
<label for="bookAuthor"><b>Author:</b></label>
<input id="bookAuthor" type="text" placeholder="Enter Author" name="bookAuthor" required>
<label for="bookPages"><b>Pages:</b></label>
<input id="bookPages" type="text" placeholder="Enter Pages" name="bookPages" required>
<button id="submit" type="button" class="btn">Add</button>
<button type="button" class="btn cancel" onclick="closeForm()">Close</button>
</form>
</div>
</div>
<script src ="script.js"></script>
</body>
</html>
CSS
.all-book-cards{
width: 20em;
display: flex;
flex-direction: column;
}
entry {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
border: black;
border-width: 3px;
border-style: groove;
padding-left: 5px;
padding-right: 5px;
margin-bottom: 2px;
}
{box-sizing: border-box;}
.open-button {
background-color: #555;
color: white;
padding:16px 20px;
border: none;
cursor: pointer;
opacity: 0.8;
position: fixed;
bottom: 23px;
right: 28px;
width: 90px;
}
.form-popup {
display: none;
position: fixed;
bottom: 0;
right: 15px;
border: 3px solid #f1f1f1;
z-index: 9;
}
.form-container {
max-width: 300px;
padding: 10px;
background-color: white;
}
.form-container input[type=text] {
width: 100%;
padding: 15px;
margin: 5px 0 22px 0;
border: none;
background: #f1f1f1;
}
.form-container input[type=text]:focus {
background-color: rgb(161, 161, 161);
outline: none;
}
.form-container .btn {
background-color: #04AA6d;
color: white;
padding: 16px 20px;
border: none;
cursor: pointer;
width: 100%;
margin-bottom: 10px;
opacity: 0.8;
}
.form-container .cancel {
background-color: red;
}
.form-container .btn:hover, .open-button:hover {
opacity: 1;
}
/* The switch - the box around the slider */
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
}
/* Hide default HTML checkbox */
.switch input {
opacity: 0;
width: 0;
height: 0;
}
/* The slider */
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: #2196F3;
}
input:focus + .slider {
box-shadow: 0 0 1px #2196F3;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
There is a few issues with your code, but the main reason you don't see any changes in the DOM is that you try to find an element by id "main", but no such element is defined.
So step one is putting an id on the element containing your books:
<div id="main" class="all-book-cards"></div>
Another issue is that you change the DOM in different ways on many places in your javascript code which makes it harder to figure out what's going on. You could change the addBookToLibrary to actually add elements to the DOM aswell. To ensure consistency with the initial books you create in the foor loop, I have changed the addBookToLibrary to handle the entire creation and removed the for loop.
//add object to library
function addBookToLibrary(Book){
myLibrary.push(Book);
let html = `
<entry>
<div>
<h3>Title: ${Book.title}</h3>
<h3>Author: ${Book.author}</h3>
<h3>Pages: ${Book.pages}</h3>
<label class="switch">
<input type="checkbox">
<span class="slider round"></span>
</label>
</div>
</entry>
`;
var newEl = document.createElement("template");
newEl.innerHTML = html.trim();
document.getElementById('main').appendChild(newEl.content.firstChild);
}
Below is a CodePen for the full example.
https://codepen.io/rasm586c/pen/PoKdNdd
Related
I created a constructor function name Book and an empty array named myLibrary as a container for these new book object instances. A separate function to create a book createBook calls a function addBookToLibrary which pushes the book into the myLibrary array, clears the form to add books, then renders the book list via the viewBookList function.
Each time I call createBook and create a new instance of Book, I'm running into a problem where the rendered list of books that show up on the page repeat all instances of Book objects created prior to the new book being added plus the one new book. For instance, I have book1, book2 and book3 that I previously created. I then add book4 so now the array has [ {book1...}, {book2...}, {book3...}, {book4...} ] objects correctly in place, but what's being rendered on the page is a scattered mess of book1, book1, book2, book1, book2, book3, book1, book2, book3, book4 as you see in the attached image. You'll see the array in the console is fine.
I initially cleared out the myLibrary array in createBook which correctly rendered the books to the page, but obviously wipes out my array which won't work because I need to be able to delete these at some point from the array. So my question is, how do I display what's in my array onto the page each time without rendering duplicates of books that were already added?
const submitButton = document.querySelector('#submit');
const btnOpenModal = document.querySelector('.open-modal');
const btnCloseModal = document.querySelector('.close-modal');
const modalElement = document.querySelector('.modal');
// let myLibrary = [
// new Book('Down and Out in Paris and London', 'George Orwell', 232, true),
// new Book('Homage to Catalonia', 'George Orwell', 202, true),
// new Book('Shooting an Elephant', 'George Orwell', 368, false),
// ];
// let count = myLibrary.length - 3;
let myLibrary = [];
let count = myLibrary.length;
// Constructor...
function Book(title, author, pages, read) {
this.title = title;
this.author = author;
this.pages = Number(pages);
this.read = Boolean(read);
// Methods
this.bookInfo = function () {
const wasRead = this.read === true ? 'read' : 'not read';
return `${this.title} written by ${this.author}, ${this.pages} pages in length was ${wasRead}.`;
};
return this.bookInfo();
}
function createBook(e) {
e.preventDefault();
// myLibrary = [];
let title = document.querySelector('#title').value;
let author = document.querySelector('#author').value;
let pages = document.querySelector('#pages').value;
let read = document.querySelector('#read').value;
// Instantiate new Book object
const newBook = new Book(title, author, pages, read); // Add ID when instantiating
addBookToLibrary(newBook);
clearForm();
viewBookList(myLibrary);
}
function addBookToLibrary(book) {
// Hide modal
removeClass();
// Add book to array
return myLibrary.push(book);
}
function clearForm() {
document.querySelector('#form').reset();
}
function setCardStyle(element, details) {
element.setAttribute(
'style',
'display: flex; flex-direction: column; justify-content: space-between; text-align: center; background-color: #fff; padding: 1em; margin: 1em 1em 1em 0; border-radius: 5px; height: 250px; width: 250px; line-height: 1.5; box-shadow: 0 5px 10px 2px rgba(0, 0, 0, 0.2);',
);
element.innerHTML = `
<h3 class="title">${details.title}</h3>
<br>
<hr>
<br>
<p>${details.author}</p>
<p>${details.pages} pages</p>
<p>${details.read === true ? 'Read' : 'Unread'}</p>`;
}
function createCard() {
let bookCard = document.createElement('div');
bookCard.classList.add('card');
bookCard.setAttribute('data-target', `${count++}`); // Set target ID
return bookCard;
}
function removeBookBtn() {
let btn = document.createElement('button');
// Style button
btn.setAttribute(
'style',
'color: red; height: 2.5em; width: 50%; border-radius: 5px; margin: 0 auto; font-weight: bold; text-transform: uppercase; cursor: pointer;',
);
btn.innerHTML = 'Delete';
return btn;
}
function handleDelete(e) {
// Get book's data-target
let bookIndex = parseInt(e.path[1].attributes[1].value);
// console.log(bookIndex);
myLibrary.splice(bookIndex, 1);
// console.log(newArray);
// myLibrary = myLibrary.filter((book, index) => index !== bookIndex);
return viewBookList(myLibrary);
}
function clearDOM(element) {
while (element.firstElementChild) {
element.firstElementChild.remove();
}
}
function viewBookList(list) {
const bookDiv = document.querySelector('.book-list');
clearDOM(bookDiv);
for (book in list) {
let bookDetails = list[book];
let renderCard = createCard();
const deleteButton = removeBookBtn();
deleteButton.addEventListener('click', handleDelete);
setCardStyle(renderCard, bookDetails);
renderCard.appendChild(deleteButton);
bookDiv.appendChild(renderCard);
}
return bookDiv;
}
function addClass() {
return modalElement.classList.add('open');
}
function removeClass() {
return modalElement.classList.remove('open');
}
// Event listeners
btnOpenModal.addEventListener('click', addClass);
btnCloseModal.addEventListener('click', removeClass);
submitButton.addEventListener('click', createBook);
#import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap');
:root {
--roboto: 'Roboto', sans-serif;
--pt: "PT Sans", sans-serif;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
font-family: var(--roboto);
}
body {
min-height: 100vh;
display: flex;
justify-content: center;
background-image: linear-gradient(to top, #a6c1ee 0%, #fbc2eb 100%);
}
.header {
text-align: center;
}
.header h1 {
color: #fff;
font-size: 4rem;
text-shadow: 1px 2px 5px #000;
}
.modal {
padding: 1em;
background-color: #fff;
}
.label {
text-transform: capitalize;
}
.btn {
font-family: "PT Sans", sans-serif;
font-size: 1.2rem;
padding: 1rem 2.5rem;
cursor: pointer;
border: none;
}
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.7);
}
.open-modal {
background: #fff;
border-radius: 5px;
box-shadow: 0 5px 10px 2px rgba(0, 0, 0, 0.2);
transition: all 0.2s linear;
}
.open-modal:hover {
box-shadow: none;
background: rgba(255, 255, 255, 0.8);
}
.modal.open {
display: flex;
}
.form {
padding: 5em;
}
.modal-container {
background-color: #fff;
width: 90%;
max-width: 450px;
position: relative;
}
.modal.open .modal-container {
animation: move 0.6s ease-out;
}
#keyframes move {
from {
transform: translateY(-50px);
}
to {
transform: translateY(0px);
}
}
.close-modal {
font-size: 3rem;
position: absolute;
top: 0;
right: 0;
background-color: #fff;
height: 40px;
width: 40px;
text-align: center;
line-height: 40px;
cursor: pointer;
transition: color 0.15s linear;
}
.close-modal:hover {
color: #f00;
}
.book-list {
display: flex;
flex-wrap: wrap;
margin-top: 1.5em;
gap: 2em;
}
/* Created in app.js */
/* .card {
} */
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles/style.css">
<script src="scripts/app.js" defer></script>
<title>Odin Library</title>
</head>
<body>
<div class="container">
<header class="header">
<h1>Library</h1>
</header>
<section class="modal open">
<div class="modal-container">
<span id="close-modal" class="close-modal">×</span>
<form action="" method="POST" name="bookForm" id="form" class="form">
<div class="form-row">
<label for="title" class="label">title: </label>
<input type="text" name="title" id="title" placeholder="Animal Farm" required>
</div>
<div class="form-row">
<label for="author" class="label">author: </label>
<input type="text" name="author" id="author" placeholder="George Orwell" required>
</div>
<div class="form-row">
<label for="pages" class="label">pages: </label>
<input type="number" name="pages" id="pages" placeholder="232" required>
</div>
<div class="form-row">
<label for="read" class="label">read: </label>
<input type="checkbox" name="read" id="read" required>
</div>
<div class="button-container">
<input type="submit" value="Create" id="submit" class="submit">
</div>
</form>
</div>
</section class="card-section">
<section>
<!-- Add plus icon -->
<button class="btn open-modal" id="open-modal">Add Book</button>
<div class="book-list">
<!-- Container for cards -->
</div>
</section>
</div>
</body>
</html>
Like the others already said on the comment, the problem is that each time you add a new book, when you try to display ti on the page you are "appending" all the books after the already existing books.
So you get something like this:
Add book 1: (1)
Add book 2: (1) (1 2)
Add book 3: (1) (1 2) (1 2 3)
The solution is to clear the container div before you update it.
Something like this (JS line 107):
const submitButton = document.querySelector('#submit');
const btnOpenModal = document.querySelector('.open-modal');
const btnCloseModal = document.querySelector('.close-modal');
const modalElement = document.querySelector('.modal');
// let myLibrary = [
// new Book('Down and Out in Paris and London', 'George Orwell', 232, true),
// new Book('Homage to Catalonia', 'George Orwell', 202, true),
// new Book('Shooting an Elephant', 'George Orwell', 368, false),
// ];
// let count = myLibrary.length - 3;
let myLibrary = [];
let count = myLibrary.length;
// Constructor...
function Book(title, author, pages, read) {
this.title = title;
this.author = author;
this.pages = Number(pages);
this.read = Boolean(read);
// Methods
this.bookInfo = function() {
const wasRead = this.read === true ? 'read' : 'not read';
return `${this.title} written by ${this.author}, ${this.pages} pages in length was ${wasRead}.`;
};
return this.bookInfo();
}
function createBook(e) {
e.preventDefault();
// myLibrary = [];
let title = document.querySelector('#title').value;
let author = document.querySelector('#author').value;
let pages = document.querySelector('#pages').value;
let read = document.querySelector('#read').value;
// Instantiate new Book object
const newBook = new Book(title, author, pages, read); // Add ID when instantiating
addBookToLibrary(newBook);
clearForm();
viewBookList(myLibrary);
}
function addBookToLibrary(book) {
// Hide modal
removeClass();
// Add book to array
return myLibrary.push(book);
}
function clearForm() {
document.querySelector('#form').reset();
}
function setCardStyle(element, details) {
element.setAttribute(
'style',
'display: flex; flex-direction: column; justify-content: space-between; text-align: center; background-color: #fff; padding: 1em; margin: 1em 1em 1em 0; border-radius: 5px; height: 250px; width: 250px; line-height: 1.5; box-shadow: 0 5px 10px 2px rgba(0, 0, 0, 0.2);',
);
element.innerHTML = `
<h3 class="title">${details.title}</h3>
<br>
<hr>
<br>
<p>${details.author}</p>
<p>${details.pages} pages</p>
<p>${details.read === true ? 'Read' : 'Unread'}</p>`;
}
function createCard() {
let bookCard = document.createElement('div');
bookCard.classList.add('card');
bookCard.setAttribute('data-target', `${count++}`); // Set target ID
return bookCard;
}
function removeBookBtn() {
let btn = document.createElement('button');
// Style button
btn.setAttribute(
'style',
'color: red; height: 2.5em; width: 50%; border-radius: 5px; margin: 0 auto; font-weight: bold; text-transform: uppercase; cursor: pointer;',
);
btn.innerHTML = 'Delete';
return btn;
}
function handleDelete(e) {
// Get book's data-target
let bookIndex = parseInt(e.path[1].attributes[1].value);
// console.log(bookIndex);
myLibrary.splice(bookIndex, 1);
// console.log(newArray);
// myLibrary = myLibrary.filter((book, index) => index !== bookIndex);
return viewBookList(myLibrary);
}
function viewBookList(list) {
const bookDiv = document.querySelector('.book-list');
bookDiv.innerHTML = "";
console.log(myLibrary);
for (book in list) {
let bookDetails = list[book];
let renderCard = createCard();
const deleteButton = removeBookBtn();
deleteButton.addEventListener('click', handleDelete);
setCardStyle(renderCard, bookDetails);
renderCard.appendChild(deleteButton);
bookDiv.appendChild(renderCard);
}
return bookDiv;
}
function addClass() {
return modalElement.classList.add('open');
}
function removeClass() {
return modalElement.classList.remove('open');
}
// Event listeners
btnOpenModal.addEventListener('click', addClass);
btnCloseModal.addEventListener('click', removeClass);
submitButton.addEventListener('click', createBook);
#import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap');
:root {
--roboto: 'Roboto', sans-serif;
--pt: "PT Sans", sans-serif;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
font-family: var(--roboto);
}
body {
min-height: 100vh;
display: flex;
justify-content: center;
background-image: linear-gradient(to top, #a6c1ee 0%, #fbc2eb 100%);
}
.header {
text-align: center;
}
.header h1 {
color: #fff;
font-size: 4rem;
text-shadow: 1px 2px 5px #000;
}
.modal {
padding: 1em;
background-color: #fff;
}
.label {
text-transform: capitalize;
}
.btn {
font-family: "PT Sans", sans-serif;
font-size: 1.2rem;
padding: 1rem 2.5rem;
cursor: pointer;
border: none;
}
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.7);
}
.open-modal {
background: #fff;
border-radius: 5px;
box-shadow: 0 5px 10px 2px rgba(0, 0, 0, 0.2);
transition: all 0.2s linear;
}
.open-modal:hover {
box-shadow: none;
background: rgba(255, 255, 255, 0.8);
}
.modal.open {
display: flex;
}
.form {
padding: 5em;
}
.modal-container {
background-color: #fff;
width: 90%;
max-width: 450px;
position: relative;
}
.modal.open .modal-container {
animation: move 0.6s ease-out;
}
#keyframes move {
from {
transform: translateY(-50px);
}
to {
transform: translateY(0px);
}
}
.close-modal {
font-size: 3rem;
position: absolute;
top: 0;
right: 0;
background-color: #fff;
height: 40px;
width: 40px;
text-align: center;
line-height: 40px;
cursor: pointer;
transition: color 0.15s linear;
}
.close-modal:hover {
color: #f00;
}
.book-list {
display: flex;
flex-wrap: wrap;
margin-top: 1.5em;
gap: 2em;
}
/* Created in app.js */
/* .card {
} */
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles/style.css">
<script src="scripts/app.js" defer></script>
<title>Odin Library</title>
</head>
<body>
<div class="container">
<header class="header">
<h1>Library</h1>
</header>
<section class="modal open">
<div class="modal-container">
<span id="close-modal" class="close-modal">×</span>
<form action="" method="POST" name="bookForm" id="form" class="form">
<div class="form-row">
<label for="title" class="label">title: </label>
<input type="text" name="title" id="title" placeholder="Animal Farm" required>
</div>
<div class="form-row">
<label for="author" class="label">author: </label>
<input type="text" name="author" id="author" placeholder="George Orwell" required>
</div>
<div class="form-row">
<label for="pages" class="label">pages: </label>
<input type="number" name="pages" id="pages" placeholder="232" required>
</div>
<div class="form-row">
<label for="read" class="label">read: </label>
<input type="checkbox" name="read" id="read" required>
</div>
<div class="button-container">
<input type="submit" value="Create" id="submit" class="submit">
</div>
</form>
</div>
</section class="card-section">
<section>
<!-- Add plus icon -->
<button class="btn open-modal" id="open-modal">Add Book</button>
<div class="book-list">
<!-- Container for cards -->
</div>
</section>
</div>
</body>
</html>
I'm starting to learn javascript and I have a simple todo application where I want to be able to drag and drop the different todo's created. A simple way to do this was with SortableJS library, but it doesn't work the way i want to. After implementing the simple sortable function, when dragging on a todo, it grabs the whole todo-list instead of a single todo
I think the issue is because I dynamically create the html, but I'm kinda stuck and would appreciate any suggestions.
//Selectors
const todoInput = document.querySelector(".todos-input"); //input for adding a todo
const todoButton = document.querySelector(".todos-button"); //add todo-button
const todoList = document.querySelector(".todos-list"); //the todo-list
//Event listeners
todoButton.addEventListener("click", addTodo);
todoList.addEventListener("click", deleteTodo);
todoList.addEventListener("click", completeTodo);
//Functions
function addTodo(event) {
//prevent form from submitting
event.preventDefault();
//create a div for the todos-list
const todoDiv = document.createElement("div");
//add classlist for styling
todoDiv.classList.add("todo");
//Create LI
const newTodo = document.createElement("li");
//output the value from the add-todo field
if (todoInput.value != "") {
newTodo.innerText = todoInput.value;
} else {
return false;
}
//classlist for styling
newTodo.classList.add("todo-item");
//append child to div
todoDiv.appendChild(newTodo);
//complete button
const completedButton = document.createElement("button");
completedButton.innerHTML = '<i class="fas fa-check"><i/>';
completedButton.classList.add("completed-btn");
todoDiv.appendChild(completedButton);
//delete button
const deletedButton = document.createElement("button");
deletedButton.innerHTML = '<i class="fas fa-trash"><i/>';
deletedButton.classList.add("deleted-btn");
todoDiv.appendChild(deletedButton);
//drag button
const dragButton = document.createElement("button");
dragButton.innerHTML = '<i class="icon fa fa-bars"><i/>';
dragButton.classList.add("drag-btn");
dragButton.classList.add("handle");
todoDiv.appendChild(dragButton);
//append div to list of todos
todoList.appendChild(todoDiv);
//clear input field after adding a new todo
todoInput.value = "";
//DRAG AND DROP
const dragArea = document.querySelector('.todos-section');
new Sortable(dragArea, {
animation: 300
});
}
//deleting todo
function deleteTodo(e) {
//grab the item, whatever we are clicking on
const item = e.target;
//delete todo
if (item.classList[0] === "deleted-btn") {
//grab the parent element of the item, which is the todolist element in this case
const todo = item.parentElement;
//remove the todo
todo.remove();
}
}
//completing todo
function completeTodo(e) {
//grab the item, whatever we are clicking on
const item = e.target;
//complete todo
if (item.classList[0] === "completed-btn") {
const todo = item.parentElement;
//use the toggle because if the element has a class, then the classList.toggle method
//behaves like classList.remove and the class is removed from the element.
//And if the element does not have the specified class
//then classList.toggle, just like classList.add, adds this class to the element.
//So it basically does the add/remove operation for us depending on the state.
todo.classList.toggle("completed-todo");
}
}
/*Apply to all elements*/
* {
box-sizing: border-box;
list-style-type: none;
margin: 0;
padding: 0;
}
body {
font-family: "Merriweather Sans", sans-serif;
background: rgba(216, 206, 206, 0.787);
}
.wrapper {
display: flex;
position: relative;
}
/* Add todos-section */
.todos-bar {
position: fixed;
top: 5%;
left: 50%;
font-size: 17px;
border: 0;
transform: translate(-50%, -50%);
padding-left: 100px;
}
.todos-bar input {
width: 600px;
height: 50px;
border: 0px;
outline: none;
font-size: 20px;
padding-left: 20px;
border-radius: 5px;
}
.todos-bar button {
position: fixed;
background: rgba(20, 33, 93, 0.952);
color: white;
font-size: 20px;
border: 0;
outline: none;
height: 50px;
padding: 10px 20px;
right: 0px;
border-radius: 0px 5px 5px 0px;
cursor: pointer;
}
.todos-bar button:hover {
background: rgb(43, 54, 73);
}
/* Todos section */
.todos-section {
display: flex;
position: fixed;
top: 15%;
left: 37%;
}
.todos-list {
width: 600px;
}
.todo {
margin: 1.5rem;
background: white;
color: black;
font-size: 1.5rem;
display: flex;
justify-content: space-between;
align-items: center;
border-radius: 5px;
padding-left: 0.5rem;
margin: 15px;
transition: all 0.5s ease;
}
.todo li {
flex: 1;
}
.todo-item {
padding: 0rem 0.5rem;
padding-left: 2.5rem;
}
.deleted-btn,
.completed-btn {
background: rgb(248, 56, 56);
color: white;
border: none;
padding: 1rem;
cursor: pointer;
font-size: 1rem;
}
.completed-btn {
background: green;
}
.deleted-btn {
border-radius: 0px 5px 5px 0px;
}
.drag-btn {
display: block;
position: absolute;
background: white;
border: 2px solid white;
}
.fa-bars {
padding: 5px;
margin: 2px;
cursor: pointer;
}
.fa-trash,
.fa-check {
pointer-events: none;
}
.completed-todo {
text-decoration: line-through;
opacity: 0.5;
}
<head>
<link rel="stylesheet" href="style.css">
<script src="https://kit.fontawesome.com/47440aba67.js" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.14.0/Sortable.min.js"></script>
</head>
<body>
<div class="wrapper">
<!--ADD TODO-->
<div class="todos-bar">
<input type="text" class="todos-input" placeholder="Add to list...">
<button class="todos-button" type="submit"><i class="fas fa-plus"></i></button>
</div>
<!--TODO LIST-->
<div class="todos-section">
<ul class="todos-list"></ul>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
According to the documentation,
You can use any element for the list and its elements, not just ul/li
What you've implemented actually fits in this description since there is a ul with div tags inside. However, you are not referencing the right element in the dragArea, because it should be the direct parent (.todos-list) of your desired draggable children.
So, change it to .todos-list and in addition pass handle property to Sortable constructor to reference the icon in which you want to drag.
const dragArea = document.querySelector('.todos-list');
new Sortable(dragArea, {
animation: 300,
handle: '.fa-bars'
})
Working example
So, i am having an issue with a div, when another div is generated (via javascript) below it, it is changing the size of the div.
// for the side nav message list
const chatList = function(list) {
let br = document.createElement("br")
for (let index in list) {
try {
let chat = list[index]
let chatBodyParent = document.createElement("div")
chatBodyParent.onclick = function() {
$("#message-list").empty()
api.listMessages(chat.chat.id)
document.getElementById("message-list").channelId = chat.chat.id
}
chatBodyParent.id = `chat-body-${chat.chat.id}`
let chatBody = document.createElement("div")
chatBody.className = "chat-body"
let chatImg = document.createElement("img")
chatImg.src = chat.chat.cover
if (!chat.chat.cover && chat.chat.type == 1) {
chatImg.src = "/dump/pfp.svg"
}
if (!chat.chat.cover && chat.chat.type == 3) {
chatImg.src = "/dump/public.png"
}
chatImg.className = "chat-img"
chatImg.setAttribute("align", "left")
chatBody.appendChild(chatImg)
let chatInfoContainer = document.createElement("div")
chatInfoContainer.className = "chat-info-container"
let chatName = document.createElement("span")
chatName.className = "chat-name"
chatName.innerText = chat.chat.title
chatInfoContainer.appendChild(chatName)
chatInfoContainer.appendChild(br.cloneNode(true))
let chatMessageContent = document.createElement("span")
chatMessageContent.className = "chat-message-content"
chatMessageContent.id = `chat-message-content-${chat.chat.id}`
let messageContent
if (chat.message) {
let long = false;
if (chat.message.text.length >= 30) {
long = true
}
messageContent = chat.message.text.substring(0, 30)
if (long) {
messageContent += "..."
}
} else if (chat.type == "file") {
messageContent = chat.user.nick + " sent a file"
}
chatMessageContent.innerText = messageContent
chatInfoContainer.appendChild(chatMessageContent)
chatBody.appendChild(chatInfoContainer)
chatBodyParent.appendChild(chatBody)
document.getElementById("chat-list").appendChild(chatBodyParent)
} catch {
console.log(list[index])
}
}
}
.sidenav {
height: 100%;
width: 15%;
position: fixed;
z-index: 1;
top: 0;
left: 0;
background-color: var(--lightish-grey);
overflow-x: hidden;
padding-top: 20px;
}
.sidenav a {
padding: 6px 8px 6px 16px;
text-decoration: none;
font-size: 25px;
color: #818181;
display: block;
}
.sidenav a:hover {
color: #f1f1f1;
}
.main {
margin-left: 15%;
padding: 0px 10px;
overflow-x: hidden;
}
#media screen and (max-height: 450px) {
.sidenav {padding-top: 15px;}
.sidenav a {font-size: 18px;}
}
::-webkit-scrollbar {
color: var(--grey);
}
::-webkit-scrollbar-corner {
color: var(--grey);
}
::-webkit-scrollbar-track {
color: var(--grey);
}
.menu {
width: 90%;
min-width: 90%;
height: 200px;
margin-left: 5%;
margin-right: 5%;
background-color: var(--menu-grey);
padding-top: 10px;
padding-bottom: 5px;
font-family: "FontRegular";
}
.chat-bar {
position: fixed;
bottom: 1%;
width: 50%;
height: 3.5%;
padding: 0px 5px;
margin: 8px 0;
display: inline-block;
border-top: hidden;
border-left: hidden;
border-right: hidden;
border-bottom: solid var(--light-grey);
box-sizing: border-box;
background-color: var(--grey);
color: var(--light-grey);
font-family: "FontRegular";
}
.chat-bar:focus {
outline-width: 0;
}
.chat-body {
width: 90%;
height: 50px;
margin-left: 5%;
border: 3px;
border-top: hidden;
border-left: hidden;
border-right: hidden;
/*border-bottom: solid var(--light-grey);*/
padding-top: 10px;
padding-bottom: 5px;
font-family: "FontRegular";
}
.chat-body:hover {
opacity: 0.8;
cursor:pointer;
}
.chat-body:focus {
opacity: 0.8;
}
.chat-img {
height: 50px;
width: auto;
border-radius: 50%;
}
.chat-info-container {
position:relative;
top: 10%;
}
<!DOCTYPE html>
<html>
<head>
<title>iFChat - Dashboard</title>
<link rel="stylesheet" href="/css/index.css">
<link rel="stylesheet" href="/css/dashboard.css"/>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script type="text/javascript" src="/js/utils.js"></script>
<script type="text/javascript" src="/js/api.js"></script>
<script type="text/javascript" src="/js/dashboard.js"></script>
</head>
<div class="sidenav">
<div id="menu" class="menu">
</div>
<div>
</div> <br><br>
<div id="chat-list">
</div>
</div>
<div class="main" id="main">
<div id="message-list" class="message-list">
</div>
<input type="text" name="chat..." id="chat-bar" class="chat-bar" placeholder="chat..." maxlength="500">
</div>
</html>
Here is an image before the chat list is loaded
Then menu is loaded with the correct size and margin
pre-load
after the chat list loads, it changes the width or margin of the div above some how, and im not sure how or why its doing that, but i cant figure it out, heres an image of after the chat list is loaded post-load
i have tried different margins and positioning settings but cant seem to get it to work, any help is greatly appreciated :)
edit: One possible solution may be to change the css with javascript every time the chat list is loaded, but i would like to avoid that if at all possible.
OK, so i figured out the issue, the issue occurs when enough elements pop up to trigger the scrollbar, so the fix for me was this
::-webkit-scrollbar {
display: none;
}
Because i want a user to beable to scroll, but i dont want there to be a scrollbar, My next plan is to make this static, so that it doesnt move on scroll at all. Still the issue was arising when my (invisible scroll bar, that still had a width) was appearing. Gotta watch out for hidden elements.
First of all i want to point out that i saw that there are similar posts about tracking event listeners but in my case i just couldn't figure it out. I am familliar with event.target property but in my case i just couldn't make it.
So this is my code snippet:
const taskListSection = document.querySelector('.task-list-section');
const taskListAddModal = document.querySelector('.task-list-add-modal');
const confirmTaskAddBtn = document.getElementById('add-list');
const cancelTaskAddBtn = document.getElementById('cancel-add-list');
const addTaskBtn = document.getElementById('add-task');
const titleInput = document.getElementById('title');
const descriptionInput = document.getElementById('description');
const timeInput = document.getElementById('time');
const clearUserInput = () => {
titleInput.value = '';
descriptionInput.value = '';
timeInput.value = '';
};
const taskListAddModalHandler = () => {
const taskList = taskListSection.querySelectorAll('li');
taskListAddModal.classList.toggle('visible');
addTaskBtn.classList.toggle('visible');
taskList.forEach((list) => {
list.classList.toggle('visible');
});
clearUserInput();
};
const confirmAddTask = () => {
const newTask = document.createElement('li');
const taskList = taskListSection.querySelectorAll('li');
const titleInputValue = titleInput.value;
const descriptionInputValue = descriptionInput.value;
const timeInputValue = timeInput.value;
if(titleInputValue.trim() === ''){
alert('Please enter a title of your task!');
return;
}
newTask.className = 'visible';
newTask.innerHTML =
`<button class="check-task">C</button>
<button class="remove-task">X</button>
<h4>Title:</h4>
<p>${titleInputValue}</p>
<h4>Description:</h4>
<p>${descriptionInputValue}</p>
<h4>Time:</h4>
<p>${timeInputValue}</p>`;
taskListSection.append(newTask);
taskListAddModal.classList.remove('visible');
taskList.forEach((list) => {
list.classList.add('visible');
});
addTaskBtn.classList.toggle('visible');
clearUserInput();
};
addTaskBtn.addEventListener('click', taskListAddModalHandler);
cancelTaskAddBtn.addEventListener('click', taskListAddModalHandler);
confirmTaskAddBtn.addEventListener('click', confirmAddTask);
body{
margin: 0;
padding: 0;
box-sizing: border-box;
}
.main-wrapper{
width: 70rem;
margin: 0 auto;
border: 2px solid black;
position: relative;
}
.main-wrapper #add-task{
display: none;
}
.main-wrapper #add-task.visible{
position: absolute;
top: 150px;
right: 100px;
width: 50px;
height: 50px;
font-size: 50px;
display: flex;
justify-content: center;
align-items: center;
}
ul{
border: 1px solid black;
width: 40rem;
height: 40rem;
margin: 10rem auto;
padding: 0;
background-color: red;
overflow-x: scroll;
}
ul form{
flex-direction: column;
width: 100%;
height: 40rem;
background-color: white;
display: none;
}
ul form input[type=button]{
display: block;
margin: 10px auto;
}
ul form.visible{
display: flex;
}
ul li{
display: none;
}
ul li.visible{
display: block;
width: 80%;
list-style: none;
border: 2px solid black;
margin: 10px;
position: relative;
}
ul li .check-task{
position: absolute;
width: 30px;
height: 30px;
top: 30px;
right: 30px;
}
ul li .remove-task{
position: absolute;
width: 30px;
height: 30px;
bottom: 30px;
right: 30px;
}
ul li.checked{
background-color: green;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<section class="main-wrapper">
<button id="add-task" class="visible">+</button>
<ul class="task-list-section">
<form class="task-list-add-modal">
<label for="title">Title:</label>
<input type="text" id="title">
<label for="description">Description:</label>
<textarea type="text" id="description" maxlength="100"></textarea>
<label for="time">Time:</label>
<input type="text" id="time">
<div class="to-do-list-confirmation">
<input type="button" id="add-list" value="ADD">
<input type="button" id="cancel-add-list" value="CANCEL">
</div>
</form>
</ul>
</section>
<script src="app.js"></script>
</body>
</html>
I have a problem to track which button 'C' on which 'li' element was clicked. So the logic behind this would be that when i click on 'C' button on certain li element that was created i want THAT 'li' element to get class named 'checked' (class 'checked' will provide green background to that 'li' element). You create 'li' element by clicking a "+" button on your top right corner than filling input elements and then by clicking ADD button. Sorry about lousy design i made it really fast just to try and explain what is my problem. I would like you to give me solution using pure JS. Thanks in advance.
Since you asked for an example, the following example is simplified to show how you can use an event parameter in your handler function to be used on the element that is being triggered in your listener. This is over simplified to show you how it is done. You will need to apply this functionality to your code, it is rather easy once you understand the concept.
Further notes in the code snippit below...
// here I am querying all the buttons in the dom
let el = document.querySelectorAll("button");
// I am running them through a loop and applying an event listner with a handler function on click
for(let i = 0; i < el.length; i++){
el[i].addEventListener('click', handler);
}
// the function passes a parameter "event" => e.
// we use the e.target to get the element being pressed
// I use a data attribute in the element being pressed
// to locate an id and affect its background color
function handler(e){
let handler = e.target.getAttribute("data-handler");
let target = document.getElementById(handler);
target.style.backgroundColor = '#d4d4d4';
}
<div id="one">Div One</div>
<div id="two">Div Two</div>
<div id="three">Div Three</div>
<div id="four">Div Four</div>
<button data-handler="one">This btn handles div one</button>
<button data-handler="two">This btn handles div two</button>
<button data-handler="three">This btn handles div three</button>
<button data-handler="four">This btn handles div four</button>
Yeah my problem was that my 'li' elements were not premade so to say, like in your example. They were created with JS so i couldnt figure out how to 'connect' button and his li element and then use target property (like you did with data-handler and id of div element). But i found a way by giving random id to my li element by using Math.random and then i used that same number as data-handler value on my button and then rest was easy. Thanks a lot you gave me a little push so i made it. It works. Here is a code snippet may be useful to someone.
const taskListSection = document.querySelector('.task-list-section');
const taskListAddModal = document.querySelector('.task-list-add-modal');
const confirmTaskAddBtn = document.getElementById('add-list');
const cancelTaskAddBtn = document.getElementById('cancel-add-list');
const addTaskBtn = document.getElementById('add-task');
const titleInput = document.getElementById('title');
const descriptionInput = document.getElementById('description');
const timeInput = document.getElementById('time');
const clearUserInput = () => {
titleInput.value = '';
descriptionInput.value = '';
timeInput.value = '';
};
const taskListAddModalHandler = () => {
const taskList = taskListSection.querySelectorAll('li');
taskListAddModal.classList.toggle('visible');
addTaskBtn.classList.toggle('visible');
taskList.forEach((list) => {
list.classList.toggle('visible');
});
clearUserInput();
};
const confirmAddTask = () => {
const newTask = document.createElement('li');
const taskList = taskListSection.querySelectorAll('li');
const titleInputValue = titleInput.value;
const descriptionInputValue = descriptionInput.value;
const timeInputValue = timeInput.value;
if(titleInputValue.trim() === ''){
alert('Please enter a title of your task!');
return;
}
newTask.className = 'visible';
let newTaskId = newTask.id = Math.random().toString();
newTask.innerHTML =
`<button data-handler = '${newTaskId}' class="check-task">C</button>
<button data-handler = '${newTaskId}' class="remove-task">X</button>
<h4>Title:</h4>
<p>${titleInputValue}</p>
<h4>Description:</h4>
<p>${descriptionInputValue}</p>
<h4>Time:</h4>
<p>${timeInputValue}</p>`;
taskListSection.append(newTask);
taskListAddModal.classList.remove('visible');
taskList.forEach((list) => {
list.classList.add('visible');
});
const checkTaskBtn = document.querySelectorAll('.check-task');
for(const btn of checkTaskBtn){
btn.addEventListener('click', taskCheck);
}
addTaskBtn.classList.toggle('visible');
clearUserInput();
};
const taskCheck = (e) => {
let handler = e.target.getAttribute("data-handler");
let target = document.getElementById(handler);
target.classList.toggle('checked');
}
addTaskBtn.addEventListener('click', taskListAddModalHandler);
cancelTaskAddBtn.addEventListener('click', taskListAddModalHandler);
confirmTaskAddBtn.addEventListener('click', confirmAddTask);
body{
margin: 0;
padding: 0;
box-sizing: border-box;
}
.main-wrapper{
width: 70rem;
margin: 0 auto;
border: 2px solid black;
position: relative;
}
.main-wrapper #add-task{
display: none;
}
.main-wrapper #add-task.visible{
position: absolute;
top: 150px;
right: 100px;
width: 50px;
height: 50px;
font-size: 50px;
display: flex;
justify-content: center;
align-items: center;
}
ul{
border: 1px solid black;
width: 40rem;
height: 40rem;
margin: 10rem auto;
padding: 0;
background-color: red;
overflow-x: scroll;
}
ul form{
flex-direction: column;
width: 100%;
height: 40rem;
background-color: white;
display: none;
}
ul form input[type=button]{
display: block;
margin: 10px auto;
}
ul form.visible{
display: flex;
}
ul li{
display: none;
}
ul li.visible{
display: block;
width: 80%;
list-style: none;
border: 2px solid black;
margin: 10px;
position: relative;
}
ul li .check-task{
position: absolute;
width: 30px;
height: 30px;
top: 30px;
right: 30px;
}
ul li .remove-task{
position: absolute;
width: 30px;
height: 30px;
bottom: 30px;
right: 30px;
}
ul li.checked{
background-color: green;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<section class="main-wrapper">
<button id="add-task" class="visible">+</button>
<ul class="task-list-section">
<form class="task-list-add-modal">
<label for="title">Title:</label>
<input type="text" id="title">
<label for="description">Description:</label>
<textarea type="text" id="description" maxlength="100"></textarea>
<label for="time">Time:</label>
<input type="text" id="time">
<div class="to-do-list-confirmation">
<input type="button" id="add-list" value="ADD">
<input type="button" id="cancel-add-list" value="CANCEL">
</div>
</form>
</ul>
</section>
<script src="app.js"></script>
</body>
</html>
I do my ToDo list. (I learn vanilla JS). And I have some problem with saving some items to localStorage. When I use Google chrome(F12), I see undefiend. Maybe, I do not save correctly to localStorage. I tried to change var task to array, but it does not help. Pleas, show me my mistakes. I know, my code must be rewritten, it is my first code on JS. P.s. in console (in stackOverflow) I have that error
{
"message": "Uncaught SyntaxError: Unexpected identifier",
"filename": "https://stacksnippets.net/js",
"lineno": 348,
"colno": 6
}
but in my browser not.
var task = document.querySelector("ul");
var forTask;
function toLocal(){
forTask = task.innerHTML;
localStorage.setItem("forLocal",forTask);
}
function newElement(newChild) {
let btnDel= document.createElement("button");
btnDel.className = "fa fa-trash-o";
let myEd = document.getElementById("myEdit");
let spanClose1 = document.getElementsByClassName("close1")[0];
let spanRedact = document.getElementsByClassName("redact")[0];
let myDel = document.getElementById("myDelete");
let spanClose = document.getElementsByClassName("close")[0];
let spanYes = document.getElementsByClassName("yes")[0];
//create button
let divWithBut = document.createElement("div");
divWithBut.className = "forButt";
let btnRedact = document.createElement("button");
btnRedact.className = "fa fa-pencil";
//redact but
btnRedact.onclick = function(){
myEd.style.display = "block";
let editText = document.getElementById("editText");
let divWithText = divWithBut.parentElement.getElementsByClassName("todoPost")[0];
editText.value = divWithText.innerHTML;
editText.currentTarget;
spanRedact.onclick = function(){
divWithText.textContent = editText.value;
divWithText.className = "todoPost";
myEd.style.display = "none";
};
spanClose1.onclick = function() {
myEd.style.display = "none";
};
}
/*************************** */
/*done but*/
let doneBut = document.createElement("button");
doneBut.className = "fa fa-check-circle-o";
doneBut.onclick = function(){
let divWithText = divWithBut.parentElement.getElementsByClassName("todoPost")[0];
divWithText.classList.toggle("checked");
}
/******************* */
divWithBut.appendChild(btnRedact);
divWithBut.appendChild(doneBut);
divWithBut.appendChild(btnDel);
/******************/
//for index
let indexDiv = document.createElement("div");
indexDiv.className = "indexDiv";
let numbInd = 1;
indexDiv.innerHTML = numbInd;
/*********************************** */
//create arrow
let divWithArrow = document.createElement("div");
divWithArrow.className = "myArrow";
let arrowUP = document.createElement("i");
arrowUP.className = "fa fa-chevron-up";
let arrowDown = document.createElement("i");
arrowDown.className = "fa fa-chevron-down";
divWithArrow.appendChild(arrowUP);
divWithArrow.appendChild(arrowDown);
//for date
let date = new Date();
let curr_date = date.getDate();
let curr_month = date.getMonth()+1;
let curr_year = date.getFullYear();
let curr_hour = date.getHours();
let curr_minutes = date.getMinutes();
let d = (curr_date + "." + curr_month + "." + curr_year+"<br>"+curr_hour+":"+curr_minutes);
let divTime = document.createElement("div");
divTime.style.textAlign = "center";;
divTime.innerHTML = d;
//***************************/
let div1 = document.createElement("div");
div1.className = "timeComent";
let myli = document.createElement("li");
myli.className = "todoPost";
let addField = document.getElementById("addField").value;
task = document.createTextNode(addField);
myli.appendChild(task);
div1.appendChild(divTime);
div1.appendChild(indexDiv);
div1.appendChild(divWithArrow);
div1.appendChild(myli);
divWithBut.style.display = "flex";
div1.appendChild(divWithBut);
if (addField === '') {
alert("You must write something!");
} else {
document.getElementById("forToDo").appendChild(div1);
toLocal();
}
document.getElementById("addField").value = "";
//delete but
btnDel.onclick = function(){
myDel.style.display = "block";
spanClose.onclick = function() {
myDel.style.display = "none";
};
spanYes.onclick = function() {
myDel.style.display = "none";
div1.remove();
};
}
toLocal();
}
if(localStorage.getItem("forLocal")){
task.innerHTML = localStorage.getItem("forLocal");
}
*{
margin: 0;
padding: 0;
}
header{
width: 100%;
display: flex;
align-items: center;
align-content: center;
justify-content: center;
overflow: auto;
}
.firstBar{
width: 100%;
display: flex;
align-items: center;
align-content: center;
justify-content: center;
overflow: auto;
}
.indexDiv{
font-style: normal;
text-align: center;
color: #fff;
width: 15px;
height: 20px;
margin: 10px;
background-color: #888;
}
.fafaArrow{
font-size: 24px;
color: #000;
}
.timeComent{
margin-top: 15px;
margin-bottom: 15px;
display: flex;
justify-content:center;
align-items: center;
}
.numberpost{
padding: 5px;
color: rgb(255, 255, 255);
background: rgb(141, 112, 112);
}
.todoPost{
background-color: #eee;
width: 50%;
margin: 5px;
overflow: auto;
text-align: justify;
}
.shadow {
background: rgba(102, 102, 102, 0.5);
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
display: none;
}
.window {
width: 300px;
height: 50px;
text-align: center;
padding: 15px;
border: 3px solid #0000cc;
border-radius: 10px;
color: #0000cc;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
background: #fff;
}
.shadow:target {display: block;}
.redact {
display: inline-block;
border: 1px solid #0000cc;
color: #0000cc;
margin: 10px;
text-decoration: none;
background: #f2f2f2;
font-size: 14pt;
cursor:pointer;
right: 0;
top: 0;
padding: 12px 16px 12px 16px;
}
.redact:hover {
background-color: #68f462;
color: white;}
.close{
display: inline-block;
border: 1px solid #0000cc;
color: #0000cc;
margin: 10px;
text-decoration: none;
background: #f2f2f2;
font-size: 14pt;
cursor:pointer;
right: 0;
top: 0;
padding: 12px 16px 12px 16px;
}
.close:hover{
background-color: #f44336;
color: white;
}
/* Style the close button */
.close3 {
position: absolute;
right: 0;
top: 0;
padding: 12px 16px 12px 16px;
}
.yes {
display: inline-block;
border: 1px solid #0000cc;
color: #0000cc;
margin: 10px;
text-decoration: none;
background: #f2f2f2;
font-size: 14pt;
cursor:pointer;
right: 0;
top: 0;
padding: 12px 16px 12px 16px;
}
.yes:hover{
background-color: #68f462;
color: white;
}
.close1{
display: inline-block;
border: 1px solid #0000cc;
color: #0000cc;
margin: 10px;
text-decoration: none;
background: #f2f2f2;
font-size: 14pt;
cursor:pointer;
right: 0;
top: 0;
padding: 12px 16px 12px 16px;
}
.close1:hover{
background-color: #f44336;
color: white;
}
/* When clicked on, add a background color and strike out text */
div li.checked {
background: #888;
color: #fff;
text-decoration: line-through;
}
/* Add a "checked" mark when clicked on */
div li.checked::before {
content: '';
position: absolute;
border-color: #fff;
border-style: solid;
border-width: 0 2px 2px 0;
top: 10px;
left: 16px;
transform: rotate(45deg);
height: 15px;
width: 7px;
}
<!DOCTYPE html>
<html>
<head>
<title>TO DO List</title>
<link rel="stylesheet" type="text/css" href="styles/style.css" >
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
</head>
<body>
<header>
<input id="addField" type="text" size="70%" placeholder="Task" name="Task">
<button type="button" onclick="newElement()">Add</button>
</header>
<div>
<div class="firstBar">
<div class="fafaArrow">
<i class="fa fa-caret-up" ></i>
<i class="fa fa-caret-down"></i>
<input class="inptxt" type="text" size="50%" name="Task">
<i class="fa fa-filter"></i>
</div>
</div>
</div>
<ul id="forToDo" >
</ul>
<div id="myDelete" class="shadow">
<div class="window">Delete item?<br>
<span class="yes">Yes</span>
<span class="close">No</span>
</div>
</div>
<div id="myEdit" class="shadow">
<div class="window">
Edit text?<br>
<label>
<textarea id="editText"></textarea>
</label>
<span class="redact">Save</span>
<span class="close1">Cancel</span>
</div>
</div>
<script src="js/script2.js"></script>
</body>
</html>
When you add an element to the page, at a certain point you do this
task = document.createTextNode(addField);
Since task is a global variable (you declared it at the top), you're overshadowing it with the TextNode you're creating, so that when you then call toLocal and you do
forTask = task.innerHTML;
task has no innerHTML attribute, so it returns undefined.
Also, for some reason, you call toLocal again at the end of newElement. It's not the problem but it's something you may want to think about. I'm not sure it's what you want.
#TakayashiHarano gave a couple of hints to solve this, but I'm not sure what you want is just to have the latest element in the local storage. So I would re-write toLocal so that it takes a string (the text of the item) as input, writes it at the end of a JSON array (already populated with what was in the local storage previously), and puts the array back in local storage.
function toLocal(toAdd) {
let storage = localStorage.getItem('forLocal');
if (storage === null) {
storage = [];
} else {
storage = JSON.parse(storage);
}
storage.push(toAdd);
localStorage.setItem('forLocal', JSON.stringify(storage));
}
Then you should modify the part of the code that reads the local storage (the one at the end) to basically simulate adding a new item as you would do when creating a new task, but for each item in the parsed JSON coming from local storage.
To be fair, your code needs a good dose of rewriting to achieve this, so I'll just leave you with this as an exercise.
The following changes are needed.
1 - Set up two variables separably for the following task variable.
var task = document.querySelector("ul");
task = document.createTextNode(addField);
For example, "ulElement" for the first one, and "task" for the second one.
This is to prevent to override the previously defined value.
2 - Move the timing for obtaining the ul element and load localStorage.
function onReady() {
ulElement = document.querySelector("ul");
if(localStorage.getItem("forLocal")){
ulElement.innerHTML = localStorage.getItem("forLocal");
}
}
window.addEventListener('DOMContentLoaded', onReady, true);
To ensure the element existence, document.querySelector() should be called after the DOMContentLoaded event fired.
https://developer.mozilla.org/en-US/docs/Web/API/Window/DOMContentLoaded_event
3 - Delete toLocal(); in the end of the newElement() function.
As far as my testing code, there is no need this statement.