0

My program basically displays books and other information about it. It also should be able to delete said book from the library and also the array in which it is stored. This is where my problem lies, I can't seem to delete that exact book from the display and the array. I tried using array.pop() and deleteRow(), but they only delete from the bottom. Thanks in advance for the help.

let myLibrary = [];

function book(name, author, pages, hasRead) {
    this.name = name;
    this.author = author;
    this.pages = pages;
    this.read = hasRead;
}

const Book1 = new book('Harry Potter', 'J. K. Rowling', 322, 'Yes');
const Book2 = new book('Great Expectations', 'Charles Dickens', 234, 'Yes');
const Book3 = new book('To Kill a Mockingbird', 'Harper Lee', 312, 'No')
const Book4 = new book('The Great Gatsby', 'F Scott Fitzgerald', 421, 'Yes');
const Book5 = new book('Ulysses', 'James Joyce', 267, 'Yes');
myLibrary.push(Book1, Book2, Book3, Book4, Book5);

function tableHeader() {
    var html = "<table id=myTable1>";
        html += "<tr>";
        html += "<th class=top1>" + 'BOOK NAME'+ "</th>";
        html += "<th class=top2>" + 'AUTHOR' + "</th>";
        html += "<th class=top3>" + 'PAGES' + "</th>";
        html += "<th class=top4>" + 'READ?' + "</th>";
        html += "</tr>";
    html += "</table>";
    document.getElementById("top").innerHTML = html
}
tableHeader();

function viewLibrary() {
    var html = "<table id=myTable>";
    for (i = 0; i < myLibrary.length; i++) {
        html += "<tr>";
        html += "<td>" + [i+1] + "</td>";
        html += "<td class=box1>" + myLibrary[i].name + "</td>";
        html += "<td class=box2>" + myLibrary[i].author + "</td>";
        html += "<td class=box3>" + myLibrary[i].pages + "</td>";
        html += "<td class=box4>" + myLibrary[i].read + "</td>";
        html += "<td class=del>" + 'X' + "</td>";
        html += "</tr>";
    }
    html += "</table>";
    document.getElementById("box").innerHTML = html;
}
viewLibrary();

function deleteBook() {
    var button = document.querySelectorAll('.del');
    var table = document.getElementById('myTable');
    var rowCount = table.rows.length;
    for (var i = 0; i < rowCount; i++) {
        button[i].addEventListener('click', function () {
            table.deleteRow(this);
            console.log(event);
            myLibrary.pop();
        })
    }
}
deleteBook();

function addModal() {
    var btn = document.getElementById("new-book");
    var modal = document.getElementById("myModal");
    var span = document.getElementsByClassName("close")[0];

    btn.onclick = function () {
        modal.style.display = "block";
    }
    span.onclick = function () {
        modal.style.display = "none";
    }
}
addModal();

function addBookToLibrary() {
    let name = document.getElementById('name').value;
    let author = document.getElementById('author').value;
    let pages = document.getElementById('pages').value;
    let read = document.getElementById('dropdown').value;
    var submit = document.getElementById("submit");
    var modal = document.getElementById("myModal");
    if (name === '' || author === '' || pages === '' || read === '') {
        alert("Fill out all values.")
        return false;
    } else {
        submit.onclick = function () {
            modal.style.display = "none";
        }
        const NewBook = new book(name, author, pages, read);
        myLibrary.push(NewBook);
        console.log(NewBook);
        viewLibrary();
        document.getElementById('name').value = '';
        document.getElementById('author').value = '';
        document.getElementById('pages').value = '';
        document.getElementById('dropdown').value = '';
        deleteBook();
    }
}

let x = document.querySelectorAll('button');
x.forEach(button => {
    button.addEventListener('click', () => {
        let selection = button.innerHTML;
        switch (selection) {
            case "Submit":
                addBookToLibrary();
                break;
        }
    })
})
body {
    background-color: #edc4b3;
}

/* The Modal (background) */
.modal {
    display: none;
    position: fixed;
    z-index: 1;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    overflow: auto;
    background-color: rgb(0, 0, 0);
    background-color: rgba(0, 0, 0, 0.4);
}

/* Modal Content/Box */
.modal-content {
    background-color: #eddcd2;
    margin: 15% auto;
    padding: 20px;
    border: 1px solid #888;
    width: 40%;
}

/* The Close Button */
.close {
    color: #aaa;
    float: right;
    font-size: 28px;
    font-weight: bold;
}

.close:hover,
.close:focus {
    color: black;
    text-decoration: none;
    cursor: pointer;
}

table {
    margin-left: 14.5%;
    margin-top: 5%;
    width: 70%;
    background-color: #edddd4;
    border: 5px solid #8d99ae;
    border-collapse: collapse;
    margin-top: 2%;
}

#top {
    color: #9d6b53;
}

#box {
    margin-top: -1.5%;
}

.line {
    color: #774936;
}

td {
    padding-bottom: 10px;
    padding-top: 10px;
    padding-left: 10px;
}

h1 {
    text-align: center;
    text-transform: uppercase;
    font-family: 'Notable', sans-serif;
}

h2 {
    text-align: center;
    text-decoration: underline;
}

#new-book {
    margin-left: 79%;
    background-color: #888;
    cursor: pointer;
    margin-top: 2%;
    padding: 5px 5px;
    font-weight: bold;
}

/*tr:first-child { font-weight: bold }*/

#name, #author, #pages {
    width: 60%;
    border: 0;
    border-bottom: 1px solid black;
    font-size: 1rem;
    color: black;
    background-color: #eddcd2;
    font-family: 'Times New Roman', Times, serif;
    padding-top: 5px;
    padding-bottom: 5px;
    padding-left: 2px;
    padding-right: 2px;
}

td {
    font-family: 'Source Code Pro', monospace; 
}

#author {
    width: 58.5%;
}

#pages {
    width: 61.3%;
}

#dropdown {
    width: 28%;
    background-color: #edddd4;
    font-size: 17px;
    cursor: pointer;  
    box-sizing: border-box;
    margin: 0;
    border: 1px solid black;
    padding-top: 5px;
    padding-bottom: 5px;
}

#submit {
    background-color: black;
    border: none;
    color: white;
    padding: 16px 32px;
    text-decoration: none;
    margin: 4px 2px;
    cursor: pointer;
    width: 30%;    
    margin-left: 35%;
}

input:focus,
select:focus,
textarea:focus,
button:focus {
    outline: none;
}

.del {
    color: red;
    cursor: pointer;
}
<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>Library</title>
    <link rel="stylesheet" href="main.css">
</head>

<body>
    <button id='new-book'>Add Book</button>

    <section id="top"></section>
    <section id="box"></section>

    <div id="myModal" class="modal">
        <!-- Modal content -->
        <div class="modal-content">
            <span class="close">&times;</span>
            <h2>Add a new book</h2>

            <label for="name">Name of the Book:</label>
            <input type="text" id="name" name="fname" required><br><br>
            <label for="author">Name of the Author:</label>
            <input type="text" id="author" name="lname" required><br><br>
            <label for="pages">Number of Pages:</label>
            <input type="number" id="pages" name="pname" required><br><br>

            <p>Have you read this book?</p>
            <select id="dropdown" required>
                <option value="" disabled selected>Select your option</option>
                <option value="Yes">Yes</option>
                <option value="No">No</option>
            </select><br><br>

            <button id="submit">Submit</button>
        </div>
    </div>

    <script src="main.js"></script>
</body>

</html>
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
aasonu
  • 95
  • 4
  • 11

4 Answers4

3

I would suggest a different approach. Instead of creating your new elements through string concatenation and .innerHTML (which has performance and security implications), use the DOM API to create your new elements and then just attach an event handler to the dynamically created cells that contain the X.

See additional comments inline below:

let headers = ["name", "author", "pages", "read"];

// Get your DOM references just once, not every time
// your event handlers execute and make sure to always
// reference the DOM element, rather than a property
// of the DOM element. That way, if you ever decide 
// that you need a different property value, you won't
// have to scan the DOM for the element reference again.
let box = document.getElementById("box");
var btn = document.getElementById("new-book");
var modal = document.getElementById("myModal");
let closeModal = document.querySelector("span.close");
let name = document.getElementById('name');
let author = document.getElementById('author');
let pages = document.getElementById('pages');
let read = document.getElementById('dropdown');
var submit = document.getElementById("submit");
var modal = document.getElementById("myModal");

// DO NOT USE GETELEMENTSBYCLASSNAME and especially
// pass an idex to the first element within it:
// var span = document.getElementsByClassName("close")[0];
// Read this for details: https://stackoverflow.com/questions/54952088/how-to-modify-style-to-html-elements-styled-externally-with-css-using-js/54952474#54952474
// Instead, use .querySelector()
var span = document.querySelector(".close");

// Bind the "buttons" that show/hide the modal
// to a common function that does that.
btn.addEventListener('click', showHideModal);
closeModal.addEventListener('click', showHideModal);

function showHideModal(){
  // No need for if/then. Just toggle the use of
  // the .hidden CSS class
  modal.classList.toggle("hidden");
}

submit.addEventListener('click', addBookToLibrary);

function book(name, author, pages, hasRead) {
    this.name = name;
    this.author = author;
    this.pages = pages;
    this.read = hasRead;
}

// No need to create variables to store new book references,
// and then push them into the Array. Just put the new books 
// directly into the new  Array.
let myLibrary = [
  new book('Harry Potter', 'J. K. Rowling', 322, 'Yes'),
  new book('Great Expectations', 'Charles Dickens', 234, 'Yes'), 
  new book('To Kill a Mockingbird', 'Harper Lee', 312, 'No'),
  new book('The Great Gatsby', 'F Scott Fitzgerald', 421, 'Yes') ,
  new book('Ulysses', 'James Joyce', 267, 'Yes')
];

function makeLibrary() {
  // Get rid of the prior table (if exists)
  if(document.getElementById("myTable")){
    document.getElementById("myTable").remove();
  }
  
  // Instead of creating concatenated strings of HTML, which gets
  // messy and hard to maintain, create DOM objects and set their
  // propeties.
  let tbl = document.createElement("table");
  tbl.id = "myTable";
  
  for (i = 0; i < myLibrary.length; i++) {
    let row = document.createElement("tr");
    let numCell = document.createElement("td");
    numCell.textContent = i + 1;
    row.appendChild(numCell);
      
      // Loop over the headers array to build the row cells
      headers.forEach(function(header, index){
        let cell = document.createElement("td");
        cell.textContent = myLibrary[i][header];
        cell.classList.add("box" + (index + 1));
        row.appendChild(cell); // Add the cell to the row
      });
      
      let delCell = document.createElement("td");
      delCell.textContent = "X";
      delCell.classList.add("del");
      
      // Because the elements are now being created as elements
      // and not strings, you can set up even binding on them.
      delCell.addEventListener("click", function(){
        myLibrary.splice(i, 1);  // Remove book from library
        this.closest("tr").remove(); // Remove row
      });
      
      row.appendChild(delCell); // Add cell to row
      tbl.appendChild(row);     // Add row to table
      box.appendChild(tbl);     // Add table to section
    }
}

makeLibrary();

function addBookToLibrary() {
  if (name.value === '' || author.value === '' || pages.value === '' || read.value === '') {
    alert("Fill out all values.")
    return false;
  } else {
    myLibrary.push(new book(name.value, author.value, pages.value, read.value));
    makeLibrary();    
    
    name.value = '';
    author.value = '';
    pages.value = '';
    dropdown.value = '';
    showHideModal();
  }
}
body {
    background-color: #edc4b3;
}

/* Added classes */
h1.addBook { font-size: 1em; }
.hidden { display:none; }

/* The Modal (background) */
.modal {
    position: fixed;
    z-index: 1;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    overflow: auto;
    background-color: rgb(0, 0, 0);
    background-color: rgba(0, 0, 0, 0.4);
}

/* Modal Content/Box */
.modal-content {
    background-color: #eddcd2;
    margin: 15% auto;
    padding: 20px;
    border: 1px solid #888;
    width: 40%;
}

/* The Close Button */
.close {
    color: #aaa;
    float: right;
    font-size: 28px;
    font-weight: bold;
}

.close:hover,
.close:focus {
    color: black;
    text-decoration: none;
    cursor: pointer;
}

table {
    margin-left: 14.5%;
    margin-top: 5%;
    width: 70%;
    background-color: #edddd4;
    border: 5px solid #8d99ae;
    border-collapse: collapse;
    margin-top: 2%;
}

#top {
    color: #9d6b53;
}

#box {
    margin-top: -1.5%;
}

.line {
    color: #774936;
}

td {
    padding-bottom: 10px;
    padding-top: 10px;
    padding-left: 10px;
}

h1 {
    text-align: center;
    text-transform: uppercase;
    font-family: 'Notable', sans-serif;
}

h2 {
    text-align: center;
    text-decoration: underline;
}

#new-book {
    margin-left: 79%;
    background-color: #888;
    cursor: pointer;
    margin-top: 2%;
    padding: 5px 5px;
    font-weight: bold;
}

/*tr:first-child { font-weight: bold }*/

#name, #author, #pages {
    width: 60%;
    border: 0;
    border-bottom: 1px solid black;
    font-size: 1rem;
    color: black;
    background-color: #eddcd2;
    font-family: 'Times New Roman', Times, serif;
    padding-top: 5px;
    padding-bottom: 5px;
    padding-left: 2px;
    padding-right: 2px;
}

td {
    font-family: 'Source Code Pro', monospace; 
}

#author {
    width: 58.5%;
}

#pages {
    width: 61.3%;
}

#dropdown {
    width: 28%;
    background-color: #edddd4;
    font-size: 17px;
    cursor: pointer;  
    box-sizing: border-box;
    margin: 0;
    border: 1px solid black;
    padding-top: 5px;
    padding-bottom: 5px;
}

#submit {
    background-color: black;
    border: none;
    color: white;
    padding: 16px 32px;
    text-decoration: none;
    margin: 4px 2px;
    cursor: pointer;
    width: 30%;    
    margin-left: 35%;
}

input:focus,
select:focus,
textarea:focus,
button:focus {
    outline: none;
}

.del {
    color: red;
    cursor: pointer;
}
<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>Library</title>
    <link rel="stylesheet" href="main.css">
</head>

<body>
    <button id='new-book'>Add Book</button>

    <section id="top">
       <!-- If you have static bits of HTML, don't create them
            dynamically in JavaScript, go ahead and hard-code them
            into the HTML.  This will reduce the amount of JavaScript
            and improve the speed of the page load. -->
       <table id="myTable1">
         <tr>
           <th class="top1">BOOK NAME</th>
           <th class=top2>AUTHOR</th>
           <th class=top3>PAGES</th>
           <th class=top4>READ?</th>
         </tr>
       </table>
    </section>
    <section id="box"></section>

    <div id="myModal" class="modal hidden">
        <!-- Modal content -->
        <div class="modal-content">
            <span class="close">&times;</span>
            <!-- You can't have an H2 if it's not a child of an H1. 
                 Don't choose a heading because of how it looks, pick
                 it because it represents the right heading level and
                 then style it with CSS. -->
            <h1 class="addBook">Add a new book</h1>

            <label for="name">Name of the Book:</label>
            <input type="text" id="name" name="fname" required><br><br>
            <label for="author">Name of the Author:</label>
            <input type="text" id="author" name="lname" required><br><br>
            <label for="pages">Number of Pages:</label>
            <input type="number" id="pages" name="pname" required><br><br>

            <p>Have you read this book?</p>
            <select id="dropdown" required>
                <option value="" disabled selected>Select your option</option>
                <option value="Yes">Yes</option>
                <option value="No">No</option>
            </select><br><br>

            <button id="submit">Submit</button>
        </div>
    </div>

    <script src="main.js"></script>
</body>
</html>
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
  • Best answer IMO – r0ulito Jun 23 '20 at 15:03
  • There's an issue though, when adding a new book to the table, it creates an entirely new table, creating multiples tables on the page. – aasonu Jun 23 '20 at 16:15
  • @user3843256 I'm looking through your code for adding a book, but all I see is the code that adds the book to the array. Where is the code you are using to update the table? – Scott Marcus Jun 23 '20 at 16:27
  • I guess by creating the elements through string concatenation and .innerHTML, the table just sort of updated by itself. Any tips on how to go about this? Thanks. – aasonu Jun 23 '20 at 16:50
  • @user3843256 I've updated the answer for better efficiency and fixed the issue of a new table being created when you add a new book by simply deleting the prior table before making a new one. Please see the comments inline. – Scott Marcus Jun 23 '20 at 16:55
0
function deleteBook() {
    var button = document.querySelectorAll('.del');
    var table = document.getElementById('myTable');
    var rowCount = table.rows.length;
    for (var i = 0; i < rowCount; i++) {
        button[i].addEventListener('click', function () {
            table.deleteRow(i);

            // I think pop will delete only the last element. 
            myLibrary.splice(i, 1);
        })
    }
}
user1592129
  • 461
  • 1
  • 5
  • 16
  • 1
    I think it won't work cause the ```i``` value will be often greater than the number of rows – r0ulito Jun 23 '20 at 14:00
  • Yes you are right, If we also have table header I think i -1? – user1592129 Jun 23 '20 at 14:05
  • The point is, each time the index is incremented, this script attach a brand new eventlistener containing the highest value for ```i``` so any clicked cross will delete the last one and then the next clicked cross will trigger an error – r0ulito Jun 23 '20 at 14:07
0

this in table.deleteRow(this) refers to a HTMLTableCellElement.

According to this doc : https://www.w3schools.com/jsreF/met_table_deleterow.asp

You'll need to pass an index instead.

Edit :

What about adding a data-row attribute to your rows where you put the index, and then getting this index by using element.dataset.row

E.G :

var html = "<table id=myTable1>";
        html += "<tr data-row=" + $yourIndex + ">";
        html += "<th class=top1>" + 'BOOK NAME'+ "</th>";
        html += "<th class=top2>" + 'AUTHOR' + "</th>";
        html += "<th class=top3>" + 'PAGES' + "</th>";
        html += "<th class=top4>" + 'READ?' + "</th>";
        html += "</tr>";
    html += "</table>";
r0ulito
  • 487
  • 2
  • 13
0

Use Splice to remove the element from the array. Use this link for your reference.

your way of explanation is a little bit vague to understand.

Elango Sengottaiyan
  • 166
  • 1
  • 2
  • 13