I have a library web application I am working on. There is a form that allows the user to add a book to the library and enter information about the book. Once the book is submitted a html card is generated on the webpage with a checkbox and a remove button. The remove button works on any card, however if I then use it to try to remove a second book i receive the error
"script.js:53 Uncaught TypeError: Failed to execute 'removeChild' on 'Node': parameter 1 is not of type 'Node'.
at HTMLButtonElement.document.getElementById.onclick (script.js:53)
Any insight would be super awesome!
Javascript
// set up library
let myLibrary = [];
let bookIndex = 0;
let buttonCount = 0;
//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);
// track library index
bookIndex = bookIndex 1;
// array to hold button IDs
let buttonArray = [];
// array to hold card IDs
let cardArray = [];
let cardID = "card:" bookIndex;
cardArray.push(cardID);
buttonCount =1;
let buttonName = "button" bookIndex;
buttonArray.push(buttonName);
let html = `
<entry id=${bookIndex}>
<div>
<h3>Title: ${Book.title}</h3>
<h3>Author: ${Book.author}</h3>
<h3>Pages: ${Book.pages}</h3>
<input type="checkbox" id="readBox" name = "readBox" value="Read">
<label for"readBox">Read</label><br>
<button id=${buttonName}>Remove</button>
</div>
</entry>
`;
console.log(buttonArray);
console.log(cardArray);
var newEl = document.createElement("template");
newEl.innerHTML = html.trim();
document.getElementById('main').appendChild(newEl.content.firstChild);
//create remove button function
document.getElementById(buttonName).onclick = function() {
let newErEl = document.getElementById(bookIndex);
document.getElementById('main').removeChild(newErEl);
console.log(myLibrary);
}
}
function removeBook(Book){
myLibrary.pop(Book);
}
//test books
let lotr = new Book('The Fellowship of the Ring', 'J.R.R. Tolkein', '423 pages');
let ender = new Book('Enders Game', 'Orson Scott Card', '324 pages');
let martian = new Book('The Martian', 'Anthony Weir', '369 pages');
addBookToLibrary(lotr);
addBookToLibrary(ender);
addBookToLibrary(martian);
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);
document.getElementById("bookTitle").value = ""
document.getElementById("bookAuthor").value = ""
document.getElementById("bookPages").value = ""
}
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 id="main" 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: #006468;
}
input:focus .slider {
box-shadow: 0 0 1px #006468;
}
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%;
}
CodePudding user response:
That error states is that you are trying to remove from html something, that isn't actually html element.
For example:
null.removeChild(child);
In let newErEl = document.getElementById(bookIndex); document.getElementById('main').removeChild(newErEl);
, "bookIndex" is 3 (last index added) when the function is first called.
So instead of deleting the book you want, you always delete the one within the last index.
After that bookIndex
isn't changed (still 3 in your example) - so it tries to remove an element that doesn't exist anymore.
The solution to that is to get the actual element that we want to remove.
// set up library
let myLibrary = [];
let bookIndex = 0;
let buttonCount = 0;
//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);
// track library index
bookIndex = bookIndex 1;
// array to hold button IDs
let buttonArray = [];
// array to hold card IDs
let cardArray = [];
let cardID = "card:" bookIndex;
cardArray.push(cardID);
buttonCount =1;
let buttonName = "button" bookIndex;
buttonArray.push(buttonName);
let html = `
<entry id=${bookIndex}>
<div>
<h3>Title: ${Book.title}</h3>
<h3>Author: ${Book.author}</h3>
<h3>Pages: ${Book.pages}</h3>
<input type="checkbox" id="readBox" name = "readBox" value="Read">
<label for"readBox">Read</label><br>
<button id=${buttonName}>Remove</button>
</div>
</entry>
`;
console.log(buttonArray);
console.log(cardArray);
var newEl = document.createElement("template");
newEl.innerHTML = html.trim();
document.getElementById('main').appendChild(newEl.content.firstChild);
//create remove button function
document.getElementById(buttonName).onclick = function(ev) { //ev here is the event happening
let newErEl = ev.target.parentNode.parentNode; //ev.target - is our `remove` button that we pressed. We take its parent, that we want to remove (but since its parent is a div also has a parent that we want gone - we take the highest parent - `entry`)
document.getElementById('main').removeChild(newErEl); //then we delete the actual element
console.log(myLibrary);
}
}
function removeBook(Book){
console.log('Library', myLibrary)
myLibrary.pop(Book);
}
//test books
let lotr = new Book('The Fellowship of the Ring', 'J.R.R. Tolkein', '423 pages');
let ender = new Book('Enders Game', 'Orson Scott Card', '324 pages');
let martian = new Book('The Martian', 'Anthony Weir', '369 pages');
addBookToLibrary(lotr);
addBookToLibrary(ender);
addBookToLibrary(martian);
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);
document.getElementById("bookTitle").value = ""
document.getElementById("bookAuthor").value = ""
document.getElementById("bookPages").value = ""
}
function openForm() {
document.getElementById("myForm").style.display = "block";
}
function submitForm(){
let a = document.getElementById("bookTitle")
}
function closeForm() {
document.getElementById("myForm").style.display = "none";
}
.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: #006468;
}
input:focus .slider {
box-shadow: 0 0 1px #006468;
}
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%;
}
<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 id="main" 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>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>