Home > Software engineering >  DOM can't construct a table from a button click, but can overwrite a table written to the DOM w
DOM can't construct a table from a button click, but can overwrite a table written to the DOM w

Time:11-09

I'm working on the Library App as part of the Odin Project self-taught full stack course. Nearly done, just trying to get my data to show up as a table.

What I expect: When I hit "submit book", I want the data to populate the table, even if no table currently exists. (see line 98-99 of the js)

clearTable();
document.getElementById('table-container').appendChild(buildTable(myLibrary));

What I get instead: Hitting "submit book" does nothing.

HOWEVER, and here's the weird part: If I start the page by creating a table initially with data from a placeholder array, the submit book and clearTable() functions will work as intended, and even add to the table even as you add more books, which is what I want it to do. It just looks like for some reason it is never able to make a table at all unless I start the page with document.getElementById('table-container').appendChild(buildTable(BOOKS)); on line 170 of javascript.

Code here: https://codepen.io/Krmanski/pen/BaVpNar. (You notice that a table shows up when you open this pen. That is my placeholder table with placeholder data. I ultimately want data objects constructed when the user hits the "submit book" button to populate this space instead. They currently do when this placeholder table is in place, but when you remove it, submit book no longer clears and adds data from the myLibrary Array.

let myLibrary = [];

function Book(title, author, pages, read) {
  this.title = title;
  this.author = author;
  this.pages = pages;
  this.read = read;

  this.info = function() {
    //allows for a different message to be given by the info method depending on the 'read' boolean
    if (read === true) {
      return `${title} by ${author}, ${pages} pages, has been read`;
    } else if (read === false) {
      return `${title} by ${author}, ${pages} pages, not read`;
    } else {
      return "ERROR";
    }
  }
}

function addBookToLibrary(title, author, pages, read) {
  let newBook = new Book(title, author, pages, read);
  myLibrary.push(newBook);
  //adds the newly created Book object to the main array. 
}

function displayBooksOnPage() {
  //successfully gets book objects from the main array to populate the dom by looping through them. 

  //find the readout area in the web page and make a variable to contain it in memory
  let readout = document.querySelector("#readout");

  //loop over library array
  myLibrary.forEach(myLibrary => {
    //for each item in the array, create a div container with a class "card"
    let card = document.createElement("div");
    card.classList.add('card');
    //add these cards to the readout area so we can see them on the page.
    readout.appendChild(card);
    //for each object in myLibrary, let that key represented by "key" as you loop through them.
    for (let key in myLibrary) {
      //create a p element inside your cards to get the info to appear
      let bookText = document.createElement('p');
      //IMPORTANT
      //because we are iterating through each object key, 'key' represents the key/property we are actually currently on.
      //therefore when we put 'key' in [], it calls the VALUE of itself in that itterable.
      bookText.innerText = (`${key}: ${myLibrary[key]}`);
      card.appendChild(bookText);
    }
  });
}

function clearForm() {
  //clears form
  document.getElementById('book-form').reset();
}

//the submit-button calls a function that gets the form data when it is clicked.
const submitButton = document.querySelector("#submit-button");

submitButton.addEventListener('click', getFormData);
submitButton.addEventListener('click', function(event) {
  event.preventDefault();
});

//get form data from page so that js can use it:
function getFormData() {
  //.value grabs whatever data is actually IN the form box that the user has typed. 
  let fTitle = document.getElementById("title-input").value;
  let fAuthor = document.getElementById("author-input").value;
  let fPages = document.getElementById("pages-input").value;
  let numPages = parseInt(fPages);


  let fRead;
  if (document.getElementById("read-input").checked) {
    fRead = true;
  } else if (!document.getElementById("read-input").checked) {
    fRead = false;
  } else {
    fRead = "error";
  }

  //then use this data to create an object and add it to the library array
  addBookToLibrary(fTitle, fAuthor, numPages, fRead);

  //clear the form upon submission
  document.getElementById('book-form').reset();

  //update table
  clearTable();
  document.getElementById('table-container').appendChild(buildTable(myLibrary));

  //test logs
  console.log(myLibrary);
}

///test stuff
function buildTable(data) {
  var node = document.createElement("table");
  node.setAttribute('id', 'book-table');

  var tr = document.createElement("tr");
  //fill the headers var with an array of the keys of the object in the 0 index (first place) of the array
  var headers = Object.keys(data[0]);

  //loop through each key in the array of key names
  for (var i = 0; i < headers.length;   i) {
    var header = headers[i];
    //create table headers for the key names
    //create a new table header called th
    var th = document.createElement("th");
    //use createTextNode to set its text to the current key via "header"
    th.appendChild(document.createTextNode(header));
    //add that th to the table row (tr) you are looping through
    tr.appendChild(th);
  }

  //actually finally add this row to the table
  node.appendChild(tr);

  //now loop through each object in the array in "data"
  data.forEach(function(rowdata) {
    //for each object, make a new table row "tr" for it.
    tr = document.createElement("tr");

    for (var i = 0; i < headers.length;   i) {
      //loops through each key and uses that key to get the value
      var header = headers[i];
      var td = document.createElement("td");
      //adds the value from the key:value pair to the td via createTextNode
      td.appendChild(document.createTextNode(rowdata[header]));

      //right-align the data if it is an int
      if (typeof rowdata[header] == "number") {
        td.style.textAlign = "right";
        td.style.color = "#E50000";
      }
      tr.appendChild(td);
    }

    node.appendChild(tr);
  });

  return node;
}

function clearTable() {
  //select the table that had been added to the div container via the dom, so we remove the content and not the div itself. 
  const toDel = document.querySelector("#book-table");
  toDel.remove();
}

//build from an array of objects
var BOOKS = [{
    name: "cat in the hat",
    author: "Dr. Suess",
    pages: 12,
    read: true,
    button: "click me"
  },
  {
    name: "lorax",
    author: "Dr. Suess",
    pages: 28,
    read: false,
    button: "click me"
  },
  {
    name: "sneeches on the beaches",
    author: "Dr. Suess",
    pages: 16,
    read: true,
    button: "click me"
  },
];

//put the table right where we want it in the page.
document.getElementById('table-container').appendChild(buildTable(BOOKS));
#main-content {
  min-height: 100vh;
  display: flex;
}

#form-flexbox {
  display: flex;
  flex-direction: column;
  width: 360px;
  padding: 40px;
  max-height: 100%;
}

#book-form {
  height: 50%;
  display: block;
}

#footer {
  position: fixed;
  bottom: 0;
  margin-bottom: 40px;
}

#library-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: start;
  padding: 40px;
  flex: 1;
  width: calc(100% - 350px);
  box-sizing: border-box;
}

.banner {
  width: 50%;
  height: 200px;
  background-image: url("https://live.staticflickr.com/2314/2854849909_4e5dcadff6_k.jpg");
  background-position: 50% 50%;
  background-size: 300% 200%;
  z-index: -1;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
}

.banner-text {
  color: white;
}

#title-banner {
  font-size: 42px;
  font-weight: 800;
}

#subtitle-banner {
  font-size: 30px;
  font-weight: 400;
}


/*Table Styles*/

table {
  border-collapse: collapse;
  border: 2px solid rgb(200, 200, 200);
  letter-spacing: 1px;
  font-size: 0.8rem;
}

td,
th {
  border: 1px solid rgb(190, 190, 190);
  padding: 10px 20px;
}

th {
  background-color: rgb(235, 235, 235);
}

td {
  text-align: center;
}

tr:nth-child(even) td {
  background-color: rgb(250, 250, 250);
}

tr:nth-child(odd) td {
  background-color: rgb(245, 245, 245);
}


/* Layout construction temporary stuff */

.l0 {
  border: solid 3px black;
}

.l1 {
  border: solid 2px red;
}

.l2 {
  border: solid 1px orange;
}
<!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">
  <title>Document</title>
  <link rel="stylesheet" href="style.css">

</head>

<body id="main-content" >
  <div id="form-flexbox" >
    <header id="sidebar-header" >Form Box</header>
    <form id="book-form" onsubmit="testRec()" >
      <label for="title-input">Book Title</label>
      <input type="text" id="title-input"><br>
      <label for="author-input">Author</label>
      <input type="text" id="author-input"><br>
      <label for="pages-input">Number of Pages</label>
      <input type="text" id="pages-input"><br>
      <input type="checkbox" id="read-input">
      <label for="read-input">Have you read this book?</label><br>
      <input type="submit" id="submit-button" form="book-form" value="Submit Book">
    </form>

    <div id="footer" >
      <h3 >Fake web app project for learning purposes only</h3>
      <h3 >by <a href="https://github.com/manski117">@manski117</a></h3>
    </div>
  </div>
  <div id="library-container" >library will go here
    <div >
      <span id="title-banner" >Library</span>
      <span id="subtitle-banner" >Library</span>

    </div>
    <div id="table-container" >



      </table>
    </div>
    <div id="readout-container" >
      <p id="readout"></p>
    </div>
  </div>

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

</html>

CodePudding user response:

You HTML includes a

        <div id="table-container" >
            
            
            
            </table>
        </div>

closing </table> in the div#table-container

If you remove that, it should append the row

CodePudding user response:

The problem is that toDel.remove(); gets an error when you don't already have a table. You should check if toDel exists before trying to remove it.

function clearTable() {
  //select the table that had been added to the div container via the dom, so we remove the content and not the div itself. 
  const toDel = document.querySelector("#book-table");
  if (toDel) {
    toDel.remove();
  }
}
  • Related