So I'm new to programming, and I'm creating a website where a user can enter information about their favourite books into a table.
However I've been trying to add an edit button feature where a user can click a button on a specific cell of the table and then be prompted to write in the replacement information.
So for example if they've already entered in the name of an author, they can click the edit button next to the info in the cell and they'll be prompted to enter in the replacement author's name, and it'll then reset the info in the cell in the table.
function addBooks() {
//Below is adding the users input information to the table.
let info = document.getElementById("author").value;
let info2 = document.getElementById("title").value;
let info3 = document.getElementById("genre").value;
let info4 = document.getElementById("review").value;
document.getElementById("author").value = "";
document.getElementById("title").value = "";
document.getElementById("genre").value = "";
document.getElementById("review").value = "";
let obj = {
author: info,
title: info2,
genre: info3,
review: info4,
};
let table = document.getElementById("table");
const row = table.insertRow(1);
var cell1 = row.insertCell(0);
var cell2 = row.insertCell(1);
var cell3 = row.insertCell(2);
var cell4 = row.insertCell(3);
//Below is the delete button which deletes a specific book/row.
var deleteButton = document.createElement("button");
deleteButton.classList.add("delete");
deleteButton.type = "button";
deleteButton.textContent = "Delete Book";
deleteButton.addEventListener("click", (event) => {
var row = deleteButton.parentNode.parentNode;
row.parentNode.removeChild(row);
});
cell1.innerHTML = `${obj.author}<button >Edit</button>`;
cell2.innerHTML = `${obj.title}<button >Edit</button>`;
cell3.innerHTML = `${obj.genre}<button >Edit</button>`;
cell4.innerHTML = `${obj.review}<button >Edit</button>`;
cell4.appendChild(deleteButton);
//Below here I am trying to addEvent listeners to the edit buttons that activate a function where the user can re-edit and enter new information into a specific cell.
const editButtons = document.getElementsByClassName("edit");
for (var i = 0; i < editButtons.length; i ) {
editButtons[i].addEventListener("click", (e) => {
editButtons.parentNode.innerHTML = prompt("Enter corrected info:");
});
}
}
Above is the Javascript code, but when I click on an edit button I get this error in the console :
books.js:47 Uncaught TypeError: Cannot set properties of undefined (setting 'innerHTML')
at HTMLButtonElement.<anonymous> (books.js:47:40)
I'm not sure what this means, but I was trying to be able to edit the text content of the parentNode. Is this the right way to to access and rewrite the text in the tables cells?
Here is also my html for reference.
<body>
<div >
<h1 >Your Books</h1>
<p >Author</p>
<input type="text" id="author" />
<p >Title</p>
<input type="text" id="title" />
<p >Genre</p>
<input type="text" id="genre" />
<p >Reviews</p>
<input type="text" id="review" />
</div>
<button onclick="addBooks()" id="button">Submit</button>
<table id="table">
<tr>
<th>Author</th>
<th>Title</th>
<th>Genre</th>
<th>Reviews</th>
</tr>
</table>
<script src="books.js"></script>
</body>
I hope that I've phrased things clear enough. Thanks a lot!
CodePudding user response:
- Never use
innerHTML
from unsanitized user inputs. UsetextContent
instead. - Function
addBooks
should be namedaddBook
. Singular. - Use
<thead>
and specifically<tbody>
as your target table element to insert rows into - Assign events on Element creation.
- Create a separate
ELNew_TD
function to ease repetitive tasks - (TODO: don't use
prompt()
)
Here's a quick remake using some nifty DOM helper functions to make the code more readable:
// DOM utility functions
const ELNew = (tag, prop) => Object.assign(document.createElement(tag), prop);
const ELS = (sel, par) => (par || document).querySelectorAll(sel);
const EL = (sel, par) => (par || document).querySelector(sel);
// TASK:
const EL_author = EL("#author");
const EL_title = EL("#title");
const EL_genre = EL("#genre");
const EL_review = EL("#review");
const EL_table = EL("#table tbody");
const EL_add = EL("#button");
const ELNew_TD = (val) => {
const EL_td = ELNew("td");
const EL_span = ELNew("span", {
textContent: val
});
const EL_edit = ELNew("button", {
type: "button",
className: "delete",
textContent: "Edit",
onclick() {
val = prompt("Enter corrected info:");
val && (EL_span.textContent = val);
}
});
EL_td.append(EL_span, EL_edit);
return EL_td;
};
const addBook = () => {
const EL_tr = ELNew("tr");
const EL_btnDel = ELNew("button", {
type: "button",
textContent: "Delete",
onclick() { EL_tr.remove(); },
});
const EL_td5 = ELNew("td");
EL_td5.append(EL_btnDel);
EL_tr.append(
ELNew_TD(EL_author.value),
ELNew_TD(EL_title.value),
ELNew_TD(EL_genre.value),
ELNew_TD(EL_review.value),
EL_td5,
);
EL_table.append(EL_tr);
// Clear form
EL_author.value = "";
EL_title.value = "";
EL_genre.value = "";
EL_review.value = "";
};
EL_add.addEventListener("click", addBook);
label {display: block; padding: 5px 0;}
label span {display: inline-block; min-width: 100px;}
<div >
<h1 >Your Books</h1>
<label><span>Author</span><input type="text" id="author"></label>
<label><span>Title</span><input type="text" id="title"></label>
<label><span>Genre</span><input type="text" id="genre"></label>
<label><span>Reviews</span><input type="text" id="review"></label>
</div>
<button id="button">Add Book</button>
<table id="table">
<thead>
<tr>
<th>Author</th><th>Title</th><th>Genre</th><th>Reviews</th><th></th>
</tr>
</thead>
<tbody></tbody>
</table>
CodePudding user response:
In your loop, use the “e” parameter you are passing into the event handler function to reference the elements (e.target).