Skip to content

Commit 1118739

Browse files
refactor: apply CJ second round feedback
1 parent 23f8830 commit 1118739

1 file changed

Lines changed: 48 additions & 42 deletions

File tree

‎debugging/book-library/script.js‎

Lines changed: 48 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,83 @@
1-
// myLibrary is the main array — like a shelf that holds all book objects
2-
let myLibrary = [];
1+
// const is safer than let here because myLibrary is never reassigned —
2+
// we only call .push() and .splice() on it, which modify its contents, not the variable itself
3+
const myLibrary = [];
34

4-
// Use const for DOM references because these elements never change
5-
// "const" means: this variable will always point to the same element
5+
// Declare all shared DOM references at the top of the file
6+
// These are const because they always point to the same HTML elements
67
const titleInput = document.getElementById("title");
78
const authorInput = document.getElementById("author");
89
const pagesInput = document.getElementById("pages");
910
const readCheckbox = document.getElementById("check");
1011

1112
// Wait for the page to fully load before running any code
12-
// addEventListener is safer than window.onload because it allows
13-
// multiple listeners to be registered without overwriting each other
1413
window.addEventListener("load", function () {
1514
addDefaultBooks();
1615
render();
1716
});
1817

1918
// Add two default books so the page is not empty on first load
20-
// Renamed from "populateStorage" — that name implied localStorage usage
2119
function addDefaultBooks() {
2220
if (myLibrary.length === 0) {
23-
myLibrary.push(new Book("Robinson Crusoe", "Daniel Defoe", "252", true));
21+
// Page count is stored as a NUMBER (252, not "252") — correct data type
22+
myLibrary.push(new Book("Robinson Crusoe", "Daniel Defoe", 252, true));
2423
myLibrary.push(
25-
new Book("The Old Man and the Sea", "Ernest Hemingway", "127", true)
24+
new Book("The Old Man and the Sea", "Ernest Hemingway", 127, true)
2625
);
2726
}
2827
}
2928

3029
// Book is a constructor function — a template for creating book objects
31-
// Think of it as a blank contact card: every book fills in the same four fields
3230
function Book(title, author, pages, hasBeenRead) {
3331
this.title = title;
3432
this.author = author;
3533
this.pages = pages;
36-
// Renamed "check" to "hasBeenRead" — much clearer what this field means
3734
this.hasBeenRead = hasBeenRead;
3835
}
3936

37+
// sanitiseText trims leading and trailing spaces from a string
38+
// Prevents accepting inputs that contain only spaces as valid values
39+
function sanitiseText(value) {
40+
return value.trim();
41+
}
42+
43+
// sanitisePages converts the pages input to a safe integer
44+
// Rejects scientific notation like "3e2" which would display strangely
45+
function sanitisePages(value) {
46+
const trimmed = value.trim();
47+
// If the input contains "e" or "E", it is scientific notation — reject it
48+
if (trimmed.toLowerCase().includes("e")) {
49+
return NaN;
50+
}
51+
const parsed = Number(trimmed);
52+
if (trimmed === "" || isNaN(parsed) || parsed <= 0) {
53+
return NaN;
54+
}
55+
return Math.floor(parsed);
56+
}
57+
4058
// Called when the user clicks Submit
41-
// Renamed from "submit" to avoid conflict with the browser's built-in submit behaviour
4259
function addBook() {
43-
// Validate that all fields are filled before adding the book
44-
if (
45-
titleInput.value === "" ||
46-
authorInput.value === "" ||
47-
pagesInput.value === ""
48-
) {
49-
alert("Please fill all fields!");
60+
// Step 1: Sanitise the inputs — clean them before checking or using them
61+
const cleanTitle = sanitiseText(titleInput.value);
62+
const cleanAuthor = sanitiseText(authorInput.value);
63+
const cleanPages = sanitisePages(pagesInput.value);
64+
65+
// Step 2: Validate — reject empty strings and invalid page numbers
66+
if (cleanTitle === "" || cleanAuthor === "" || isNaN(cleanPages)) {
67+
alert("Please fill all fields with valid values!");
5068
return;
5169
}
5270

53-
// Create a new Book object from the form values
71+
// Step 3: Create and store the new book using the cleaned values
5472
const newBook = new Book(
55-
titleInput.value,
56-
authorInput.value,
57-
pagesInput.value,
73+
cleanTitle,
74+
cleanAuthor,
75+
cleanPages,
5876
readCheckbox.checked
5977
);
60-
6178
myLibrary.push(newBook);
6279

63-
// Clear the form fields after adding the book
80+
// Step 4: Clear the form fields after adding the book
6481
titleInput.value = "";
6582
authorInput.value = "";
6683
pagesInput.value = "";
@@ -70,18 +87,12 @@ function addBook() {
7087
}
7188

7289
// createReadButton builds and returns a toggle button for a single book
73-
// Extracting this into its own function makes render() easier to read
7490
function createReadButton(index) {
7591
const button = document.createElement("button");
7692
button.className = "btn btn-success";
77-
78-
// Use textContent instead of innerHTML for safety
79-
// textContent never runs any HTML or scripts — innerHTML can be dangerous
80-
// if the text comes from user input
8193
button.textContent = myLibrary[index].hasBeenRead ? "Yes" : "No";
8294

8395
button.addEventListener("click", function () {
84-
// Flip the read status: true becomes false, false becomes true
8596
myLibrary[index].hasBeenRead = !myLibrary[index].hasBeenRead;
8697
render();
8798
});
@@ -96,7 +107,8 @@ function createDeleteButton(index) {
96107
button.textContent = "Delete";
97108

98109
button.addEventListener("click", function () {
99-
// splice(index, 1) removes exactly 1 item at position "index"
110+
// Show an alert with the deleted book's title — restoring original behaviour
111+
alert(`"${myLibrary[index].title}" has been deleted.`);
100112
myLibrary.splice(index, 1);
101113
render();
102114
});
@@ -107,29 +119,23 @@ function createDeleteButton(index) {
107119
// render() redraws the entire table from the current myLibrary array
108120
function render() {
109121
const table = document.getElementById("display");
110-
const rowCount = table.rows.length;
111122

112-
// Delete all rows except the header row (row 0)
113-
for (let i = rowCount - 1; i > 0; i--) {
114-
table.deleteRow(i);
115-
}
123+
// Clear the tbody in one step — simpler and faster than deleting rows one by one
124+
const tbody = table.querySelector("tbody");
125+
tbody.innerHTML = "";
116126

117-
// Build one row per book using helper functions
118127
for (let i = 0; i < myLibrary.length; i++) {
119-
const row = table.insertRow();
128+
const row = tbody.insertRow();
120129
const titleCell = row.insertCell(0);
121130
const authorCell = row.insertCell(1);
122131
const pagesCell = row.insertCell(2);
123132
const readCell = row.insertCell(3);
124133
const deleteCell = row.insertCell(4);
125134

126-
// Use textContent for user data — never innerHTML — to prevent XSS attacks
127-
// XSS (Cross-Site Scripting) is when a malicious script gets injected into a page
128135
titleCell.textContent = myLibrary[i].title;
129136
authorCell.textContent = myLibrary[i].author;
130137
pagesCell.textContent = myLibrary[i].pages;
131138

132-
// Append the buttons created by the helper functions
133139
readCell.appendChild(createReadButton(i));
134140
deleteCell.appendChild(createDeleteButton(i));
135141
}

0 commit comments

Comments
 (0)