skip to Main Content

I want to create a remove button in each row of a table, so I created a removeButton element in JavaScript and appended it to each row. However, it moves to the last row whenever I create new rows.

Here is my table

Here is my table when i create a new row

Code:

I created a removeButton element:

const removeButton = document.createElement("button");
removeButton.innerText = "Remove book";
removeButton.className = "table__remove-btn";

And append it to each row when update the table:

function updateTable() {
  table.innerHTML =
    "<tr><th>Title</th><th>Author</th><th>Pages</th><th>Is read?</th></tr>";
  if (myLibrary.length != 0) {
    const tableContent = document.createDocumentFragment();
    myLibrary.forEach((value) => {
      const tableRow = document.createElement("tr");
      const titleCell = document.createElement("td");
      const title = document.createElement("p");
      const author = document.createElement("td");
      const pages = document.createElement("td");
      const isRead = document.createElement("td");
      title.innerText = value.title;
      titleCell.appendChild(title);
      titleCell.appendChild(removeButton); // <------------------- Here!
      titleCell.className = "table__title-cell";
      tableRow.appendChild(titleCell);
      author.innerText = value.author;
      tableRow.appendChild(author);
      pages.innerText = value.pages;
      tableRow.appendChild(pages);
      isRead.innerText = value.isRead ? "Yes" : "No";
      tableRow.appendChild(isRead);
      tableContent.appendChild(tableRow);
    });
    table.appendChild(tableContent);
  }
}

How i can solve it? I want to have the removeButton on each row

2

Answers


  1. What happens is that you create only one element using document.createElement(). When the browser reads this code, it only allocates one element in the memory, and it is rereferenced each time you run titleCell.appendChild(element), resulting in only the last row keeping this button. For this code to work the way you’d like, you have to create a new button element on each iteration of the forEach loop. I have submitted two variations below. One using a predefined function to create the buttons, and one with the code inline with updateTable().

    Predefined function

      const newRemoveButton = () => {
          const removeButton = document.createElement("button");
          removeButton.innerText = "Remove book";
          removeButton.className = "table__remove-btn";
          return removeButton;
        };

    and then you use this function inside the updateTable() function

    function updateTable() {
          table.innerHTML =
            "<tr><th>Title</th><th>Author</th><th>Pages</th><th>Is read?</th></tr>";
          if (myLibrary.length != 0) {
            const tableContent = document.createDocumentFragment();
            myLibrary.forEach((value) => {
              const tableRow = document.createElement("tr");
              const titleCell = document.createElement("td");
              const title = document.createElement("p");
              const author = document.createElement("td");
              const pages = document.createElement("td");
              const isRead = document.createElement("td");
              title.innerText = value.title;
              titleCell.appendChild(title);
              /*
                    The difference lies here. The new button is created inside of 
                    updateTable()'s scope, and therefore creates a new element for each time. 
                    WHen this code was outside of the funciton, it used the same button for each row,
                    making it so that only the last row got the button.
              */
              const removeButton = document.createElement("button");
              removeButton.innerText = "Remove book";
              removeButton.className = "table__remove-btn";
              titleCell.appendChild(removeButton); // <------------------- Here!
              // Rest of your code
              titleCell.className = "table__title-cell";
              tableRow.appendChild(titleCell);
              author.innerText = value.author;
              tableRow.appendChild(author);
              pages.innerText = value.pages;
              tableRow.appendChild(pages);
              isRead.innerText = value.isRead ? "Yes" : "No";
              tableRow.appendChild(isRead);
              tableContent.appendChild(tableRow);
            });
            table.appendChild(tableContent);
          }
        }
    

    Or, you can place all of the code inline with updateTable(), like this:

    function updateTable() {
          table.innerHTML =
            "<tr><th>Title</th><th>Author</th><th>Pages</th><th>Is read?</th></tr>";
          if (myLibrary.length != 0) {
            const tableContent = document.createDocumentFragment();
            myLibrary.forEach((value) => {
              const tableRow = document.createElement("tr");
              const titleCell = document.createElement("td");
              const title = document.createElement("p");
              const author = document.createElement("td");
              const pages = document.createElement("td");
              const isRead = document.createElement("td");
              title.innerText = value.title;
              titleCell.appendChild(title);
              const removeButton = document.createElement("button");
              removeButton.innerText = "Remove book";
              removeButton.className = "table__remove-btn";
              titleCell.appendChild(getRemoveButton()); // <------------------- Update predefined button with getRemoveButton function!
              titleCell.className = "table__title-cell";
              tableRow.appendChild(titleCell);
              author.innerText = value.author;
              tableRow.appendChild(author);
              pages.innerText = value.pages;
              tableRow.appendChild(pages);
              isRead.innerText = value.isRead ? "Yes" : "No";
              tableRow.appendChild(isRead);
              tableContent.appendChild(tableRow);
            });
            table.appendChild(tableContent);
          }
        }
    
    
    just ask any questions if you're curios of anything
    Login or Signup to reply.
    • create a new button inside the each loop. Currently you’re creating only one button and moving it around row by row. (Another solution would be eventually to cloneNode it)
    • There’s no need to re-create the TH elements if you use <thead> and <tbody>
    • No need to use if (myLibrary.length != 0) { when using forEach. It will either loop or not
    • Since you create a lot of new elements it woujld be useful to use some custom DOM utils functions like i.e: el (to target elements) and elNew() to create new ones
    • make a separate createRow function that creates a row, that way you can append a single book whenever it’s needed
    // DOM UTILS
    const el = (sel, par = document) => par.querySelector(sel);
    const elNew = (tag, prop) => Object.assign(document.createElement(tag), prop);
    
    // BOOKS APP
    const elTbody = el("#books tbody");
    
    const createRow = (book) => {
      const elTr        = elNew("tr");
      const elTdTitle   = elNew("td", { textContent: book.title });
      const elTdAuthor  = elNew("td", { textContent: book.author });
      const elTdPages   = elNew("td", { textContent: book.pages });
      const elTdIsRead  = elNew("td", { textContent: book.isRead ? "Yes" : "No" });
      const elTdActions = elNew("td");
      const elBtnRemove = elNew("button", { textContent: "Remove", onclick() { elTr.remove(); }, type: "button", className: "table__remove-btn" });
      elTdActions.append(elBtnRemove)
      elTr.append(elTdTitle, elTdAuthor, elTdPages, elTdIsRead, elTdActions);
      elTbody.append(elTr);
    };
    
    const createAll = (books) => {
      elTbody.innerHTML = "";
      books.forEach(createRow);
    };
    
    const myLibrary = [
      { title: "Harry Potter and the Philosopher's Stone", author: "J. K. Rowling", pages: 223, isRead: true },
      { title: "New Book", author: "Person", pages: 20, isRead: false }
    ];
    createAll(myLibrary);
    createRow({title: "Pro JS", author: "John Doe", pages: 1490, isRead: true});
    body { font: 1em/1.4 sans-serif; }
    table { width: 100%; }
    th, td { padding: 0.5em; border: 1px solid #5a5a5a; }
    tr { &:nth-child(odd)  td { background: #fafafa; } &:nth-child(even) td { background: #ededed; } }
    th { background: #5a5a5a; color: #fff; text-align: left; white-space: nowrap; }
    <table id="books">
      <thead>
        <tr>
          <th>Title</th>
          <th>Author</th>
          <th>Pages</th>
          <th>Is read?</th>
          <th></th>
        </tr>
      </thead>
      <tbody></tbody>
    </table>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search