Problem:
The project is to create a library using vanilla JS and HTML. I have the whole project essentially completed except two functions fail to work.
Those being the remove book and change read status, here are their code snippets respectively:
const removeBook = (e) => {
const title = e.currentTarget.innerHTML.replaceAll(
'"',
'',
'Title: ')
library.removeBook(title)
updateLibContainer()
console.log('remove');
}
const toggleRead = (e) => {
const title = e.currentTarget.innerHTML.replaceAll(
'"',
'',
'Title: ')
const book = library.getTitle(title)
book.isRead = !book.isRead
console.log('read');
updateLibContainer()
}
Here is the full write up of the JS:
// Selectors for pop up and closing pop up when x is pressed
const popUp = document.querySelector('.pop-up');
const closePopUp = document.getElementsByTagName('span')[0];
closePopUp.addEventListener('click', () => popUp.style.display = 'none')
const newBookBtn = document.querySelector('.newbtn');
newBookBtn.addEventListener('click', () => popUp.style.display = 'block')
// Constructor for books
class Book {
constructor(
title = 'Unkown',
author = 'Unkown',
pages = 0,
isRead = false
) {
this.title = title
this.author = author
this.pages = pages
this.isRead = isRead
}
}
// Constructor for Library
class Library {
constructor() {
this.books = []
}
addBook(newBook) {
this.books.push(newBook)
}
removeBook(title) {
this.books = this.books.filter((book) => book.title !== title)
}
getTitle(title) {
return this.books.find(book => book.title === title)
}
isInLibrary(title) {
return this.books.some(book => book.title === title)
}
}
const library = new Library()
// Additional UI declarations
const LibContainer = document.getElementById('lib-container')
const addBookForm = document.getElementById('add-form')
// Handling and creating book from form input data
const bookFromInput = () => {
const title = document.getElementById('title').value
const author = document.getElementById('author').value
const pages = document.getElementById('pages').value
const isRead = document.getElementById('is-read').checked
return new Book(title, author, pages, isRead)
}
// Function when form is submitted to create new book from input, check if it exists
// and close form
const addNewBook = (e) => {
e.preventDefault()
const newBook = bookFromInput()
if (library.isInLibrary(newBook.title)) {
alert('Book already exists')
} else {
library.addBook(newBook)
exitForm()
updateLibContainer()
}
}
const exitForm = () => {
addBookForm.reset()
popUp.style.display = 'none'
}
const resetLibContainer = () => {
LibContainer.innerHTML = ''
console.log('reset Container');
}
const updateLibContainer = () => {
resetLibContainer()
for (let book of library.books) {
createBookCard(book)
}
console.log('updateContainer');
}
// Creating book card
const createBookCard = (book) => {
const bookCard = document.createElement('div')
const title = document.createElement('p')
const author = document.createElement('p')
const pages = document.createElement('p')
const changeReadStatus = document.createElement('button')
const removeBookBttn = document.createElement('button')
bookCard.classList.add('book-card')
changeReadStatus.classList.add('change-read-status')
changeReadStatus.onclick = toggleRead
removeBookBttn.classList.add('remove-book')
removeBookBttn.onclick = removeBook
title.textContent = `Title: ${book.title}`
author.textContent = `Author: ${book.author}`
pages.textContent = `Pages: ${book.pages}`
removeBookBttn.textContent = 'Remove'
if (book.isRead) {
changeReadStatus.textContent = 'Read'
changeReadStatus.style.backgroundColor = '#68f364'
} else {
changeReadStatus.textContent = 'Not read'
changeReadStatus.style.backgroundColor = '#d16767'
}
bookCard.appendChild(title)
bookCard.appendChild(author)
bookCard.appendChild(pages)
bookCard.appendChild(changeReadStatus)
bookCard.appendChild(removeBookBttn)
LibContainer.appendChild(bookCard)
}
const removeBook = (e) => {
const title = e.currentTarget.innerHTML.replaceAll(
'"',
'',
'Title: ')
library.removeBook(title)
updateLibContainer()
console.log('remove');
}
const toggleRead = (e) => {
const title = e.currentTarget.innerHTML.replaceAll(
'"',
'',
'Title: ')
const book = library.getTitle(title)
book.isRead = !book.isRead
console.log('read');
updateLibContainer()
}
addBookForm.onsubmit = addNewBook
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Library</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="header">
<h1>The Library</h1>
</div>
<div class="newbtn-container">
<button class="newbtn">+ New Book</button>
</div>
<div class="pop-up">
<form class="form" action id="add-form">
<span>x</span>
<legend>New book</legend>
<input type="text" id="title" class="form" placeholder="Title">
<input type="text" id="author" class="form" placeholder="Author">
<input type="text" id="pages" class="form" placeholder="Pages">
<div class="checkbox">
<label>Read: </label>
<input type="checkbox" class="form" id="is-read">
</div>
<button class="form" id="submit">Add</button>
</form>
</div>
<div id="lib-container">
<div class="book-card">
<p>Title: 22</p>
<p>Author: 22</p>
<p>Pages: 22</p>
<button class="change-read-status" style="background-color: rgb(209, 103, 103);">Not read</button>
<button class="remove-book">Remove</button>
</div>
</div>
</body>
<script src="script.js"></script>
</html>
The book card in the HTML code above is just an example placeholder if a user were to input a book.
Now the exact issue is that both aforementioned functions just dont work at all. The toggle read function produces a Type Error on the line book.isRead = !book.isRead
.
The remove function just doesnt do its purpose at all. Ive tried the past 3 days to try and trouble shoot using the methods I have learned all to no real avail. Sorry in advance if information is missing or if this isnt formatted properly.
Solution:
The problem is that the book titles aren’t in the innerHTML
of the buttons. It’s in the title
<p>
element. So you need to find the title element associated with the clicked button.
I recommend giving the paragraph a class:
const title = document.createElement('p');
title.classList.add("title");
Then you can find the div containing the button and navigate to the corresponding title element.
There’s no need to remove "
from the title, you never added them when creating the element. You just need to remove Title:
at the beginning.
const title = e.currentTarget.closest(".book-card").querySelector(".title").innerText.replace("Title: ");
Full code
// Selectors for pop up and closing pop up when x is pressed
const popUp = document.querySelector('.pop-up');
const closePopUp = document.getElementsByTagName('span')[0];
closePopUp.addEventListener('click', () => popUp.style.display = 'none')
const newBookBtn = document.querySelector('.newbtn');
newBookBtn.addEventListener('click', () => popUp.style.display = 'block')
// Constructor for books
class Book {
constructor(
title = 'Unkown',
author = 'Unkown',
pages = 0,
isRead = false
) {
this.title = title
this.author = author
this.pages = pages
this.isRead = isRead
}
}
// Constructor for Library
class Library {
constructor() {
this.books = []
}
addBook(newBook) {
this.books.push(newBook)
}
removeBook(title) {
this.books = this.books.filter((book) => book.title !== title)
}
getTitle(title) {
return this.books.find(book => book.title === title)
}
isInLibrary(title) {
return this.books.some(book => book.title === title)
}
}
const library = new Library()
// Additional UI declarations
const LibContainer = document.getElementById('lib-container')
const addBookForm = document.getElementById('add-form')
// Handling and creating book from form input data
const bookFromInput = () => {
const title = document.getElementById('title').value
const author = document.getElementById('author').value
const pages = document.getElementById('pages').value
const isRead = document.getElementById('is-read').checked
return new Book(title, author, pages, isRead)
}
// Function when form is submitted to create new book from input, check if it exists
// and close form
const addNewBook = (e) => {
e.preventDefault()
const newBook = bookFromInput()
if (library.isInLibrary(newBook.title)) {
alert('Book already exists')
} else {
library.addBook(newBook)
exitForm()
updateLibContainer()
}
}
const exitForm = () => {
addBookForm.reset()
popUp.style.display = 'none'
}
const resetLibContainer = () => {
LibContainer.innerHTML = ''
console.log('reset Container');
}
const updateLibContainer = () => {
resetLibContainer()
for (let book of library.books) {
createBookCard(book)
}
console.log('updateContainer');
}
// Creating book card
const createBookCard = (book) => {
const bookCard = document.createElement('div')
const title = document.createElement('p')
title.classList.add("title")
const author = document.createElement('p')
const pages = document.createElement('p')
const changeReadStatus = document.createElement('button')
const removeBookBttn = document.createElement('button')
bookCard.classList.add('book-card')
changeReadStatus.classList.add('change-read-status')
changeReadStatus.onclick = toggleRead
removeBookBttn.classList.add('remove-book')
removeBookBttn.onclick = removeBook
title.textContent = `Title: ${book.title}`
author.textContent = `Author: ${book.author}`
pages.textContent = `Pages: ${book.pages}`
removeBookBttn.textContent = 'Remove'
if (book.isRead) {
changeReadStatus.textContent = 'Read'
changeReadStatus.style.backgroundColor = '#68f364'
} else {
changeReadStatus.textContent = 'Not read'
changeReadStatus.style.backgroundColor = '#d16767'
}
bookCard.appendChild(title)
bookCard.appendChild(author)
bookCard.appendChild(pages)
bookCard.appendChild(changeReadStatus)
bookCard.appendChild(removeBookBttn)
LibContainer.appendChild(bookCard)
}
const removeBook = (e) => {
const title = e.currentTarget.closest(".book-card").querySelector(".title").innerText.replace('Title: ', '')
library.removeBook(title)
updateLibContainer()
console.log('remove');
}
const toggleRead = (e) => {
const title = e.currentTarget.closest(".book-card").querySelector(".title").innerText.replace('Title: ', '')
const book = library.getTitle(title)
book.isRead = !book.isRead
console.log('read');
updateLibContainer()
}
addBookForm.onsubmit = addNewBook
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Library</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="header">
<h1>The Library</h1>
</div>
<div class="newbtn-container">
<button class="newbtn">+ New Book</button>
</div>
<div class="pop-up">
<form class="form" action id="add-form">
<span>x</span>
<legend>New book</legend>
<input type="text" id="title" class="form" placeholder="Title">
<input type="text" id="author" class="form" placeholder="Author">
<input type="text" id="pages" class="form" placeholder="Pages">
<div class="checkbox">
<label>Read: </label>
<input type="checkbox" class="form" id="is-read">
</div>
<button class="form" id="submit">Add</button>
</form>
</div>
<div id="lib-container">
<div class="book-card">
<p>Title: 22</p>
<p>Author: 22</p>
<p>Pages: 22</p>
<button class="change-read-status" style="background-color: rgb(209, 103, 103);">Not read</button>
<button class="remove-book">Remove</button>
</div>
</div>
</body>
<script src="script.js"></script>
</html>