From 9bfbf87efc1e44840a9ed73a15457195495bfd69 Mon Sep 17 00:00:00 2001 From: Mona-Eltantawy Date: Tue, 21 Apr 2026 22:51:37 +0100 Subject: [PATCH 1/9] I removed render call from inside populateStorage ( ) to avoid duplicate DOM. --- debugging/book-library/script.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/debugging/book-library/script.js b/debugging/book-library/script.js index 75ce6c1d..49b04468 100644 --- a/debugging/book-library/script.js +++ b/debugging/book-library/script.js @@ -2,7 +2,8 @@ let myLibrary = []; window.addEventListener("load", function (e) { populateStorage(); - render(); + render(); + }); function populateStorage() { @@ -16,7 +17,6 @@ function populateStorage() { ); myLibrary.push(book1); myLibrary.push(book2); - render(); } } @@ -54,7 +54,7 @@ function render() { let table = document.getElementById("display"); let rowsNumber = table.rows.length; //delete old table - for (let n = rowsNumber - 1; n > 0; n-- { + for (let n = rowsNumber - 1; n > 0; n--) { table.deleteRow(n); } //insert updated row and cells From 3ba261d3fd279628da5129d24e806777bd68e9b5 Mon Sep 17 00:00:00 2001 From: Mona-Eltantawy Date: Tue, 21 Apr 2026 22:55:46 +0100 Subject: [PATCH 2/9] Implemnted book function to define the book. --- debugging/book-library/script.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/debugging/book-library/script.js b/debugging/book-library/script.js index 49b04468..6cad961d 100644 --- a/debugging/book-library/script.js +++ b/debugging/book-library/script.js @@ -6,6 +6,13 @@ window.addEventListener("load", function (e) { }); +function Book(title, author, pages, read) { + this.title = title; + this.author = author; + this.pages = pages; + this.read = read; +} + function populateStorage() { if (myLibrary.length == 0) { let book1 = new Book("Robison Crusoe", "Daniel Defoe", "252", true); @@ -20,6 +27,9 @@ function populateStorage() { } } + + + const title = document.getElementById("title"); const author = document.getElementById("author"); const pages = document.getElementById("pages"); From fec510d0e834eb6c7d03794d6aa9644a207c40da Mon Sep 17 00:00:00 2001 From: Mona-Eltantawy Date: Wed, 22 Apr 2026 12:41:32 +0100 Subject: [PATCH 3/9] changed the if condition in the submit function. --- debugging/book-library/script.js | 64 +++++++++++++------------------- 1 file changed, 25 insertions(+), 39 deletions(-) diff --git a/debugging/book-library/script.js b/debugging/book-library/script.js index 6cad961d..7dc8f559 100644 --- a/debugging/book-library/script.js +++ b/debugging/book-library/script.js @@ -1,18 +1,10 @@ let myLibrary = []; -window.addEventListener("load", function (e) { +window.addEventListener("load", function () { populateStorage(); - render(); }); -function Book(title, author, pages, read) { - this.title = title; - this.author = author; - this.pages = pages; - this.read = read; -} - function populateStorage() { if (myLibrary.length == 0) { let book1 = new Book("Robison Crusoe", "Daniel Defoe", "252", true); @@ -24,12 +16,10 @@ function populateStorage() { ); myLibrary.push(book1); myLibrary.push(book2); + render(); } } - - - const title = document.getElementById("title"); const author = document.getElementById("author"); const pages = document.getElementById("pages"); @@ -39,16 +29,16 @@ const check = document.getElementById("check"); //via Book function and start render function function submit() { if ( - title.value == null || - title.value == "" || - pages.value == null || - pages.value == "" + title.value = ""; +author.value = ""; +pages.value = ""; +check.checked = false; ) { alert("Please fill all fields!"); return false; } else { - let book = new Book(title.value, title.value, pages.value, check.checked); - library.push(book); + let book = new Book(title.value, author.value, pages.value, check.checked); + myLibrary.push(book); render(); } } @@ -70,7 +60,7 @@ function render() { //insert updated row and cells let length = myLibrary.length; for (let i = 0; i < length; i++) { - let row = table.insertRow(1); + let row = table.insertRow(); let titleCell = row.insertCell(0); let authorCell = row.insertCell(1); let pagesCell = row.insertCell(2); @@ -81,30 +71,26 @@ function render() { pagesCell.innerHTML = myLibrary[i].pages; //add and wait for action for read/unread button - let changeBut = document.createElement("button"); - changeBut.id = i; - changeBut.className = "btn btn-success"; - wasReadCell.appendChild(changeBut); - let readStatus = ""; - if (myLibrary[i].check == false) { - readStatus = "Yes"; - } else { - readStatus = "No"; - } - changeBut.innerText = readStatus; + const index = i; - changeBut.addEventListener("click", function () { - myLibrary[i].check = !myLibrary[i].check; - render(); - }); +changeBut.addEventListener("click", function () { + myLibrary[index].check = !myLibrary[index].check; + render(); +}); + +delButton.addEventListener("click", function () { + alert(`You've deleted title: ${myLibrary[index].title}`); + myLibrary.splice(index, 1); + render(); +}); //add delete button to every row and render again let delButton = document.createElement("button"); - delBut.id = i + 5; - deleteCell.appendChild(delBut); - delBut.className = "btn btn-warning"; - delBut.innerHTML = "Delete"; - delBut.addEventListener("clicks", function () { + delButton.id = i + 5; + deleteCell.appendChild(delButton); + delButton.className = "btn btn-warning"; + delButton.innerHTML = "Delete"; + delButton.addEventListener("click", function () { alert(`You've deleted title: ${myLibrary[i].title}`); myLibrary.splice(i, 1); render(); From 5b6c13afc5845445662ded0f8e0a4b5274e11c89 Mon Sep 17 00:00:00 2001 From: Mona-Eltantawy Date: Wed, 22 Apr 2026 12:44:41 +0100 Subject: [PATCH 4/9] I created and defined the changeBut button --- debugging/book-library/script.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debugging/book-library/script.js b/debugging/book-library/script.js index 7dc8f559..bf77bf92 100644 --- a/debugging/book-library/script.js +++ b/debugging/book-library/script.js @@ -72,6 +72,12 @@ function render() { //add and wait for action for read/unread button const index = i; +let changeBut = document.createElement("button"); +changeBut.className = "btn btn-success"; +wasReadCell.appendChild(changeBut); + +let readStatus = myLibrary[i].check ? "Read" : "Not Read"; +changeBut.innerText = readStatus; changeBut.addEventListener("click", function () { myLibrary[index].check = !myLibrary[index].check; From 6677db98fe3833f5bd25a6b27833191dfe5992e6 Mon Sep 17 00:00:00 2001 From: Mona-Eltantawy Date: Wed, 22 Apr 2026 12:46:35 +0100 Subject: [PATCH 5/9] I moved the indx = i in the changeBut to come after the button --- debugging/book-library/script.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debugging/book-library/script.js b/debugging/book-library/script.js index bf77bf92..cb607cff 100644 --- a/debugging/book-library/script.js +++ b/debugging/book-library/script.js @@ -71,14 +71,14 @@ function render() { pagesCell.innerHTML = myLibrary[i].pages; //add and wait for action for read/unread button - const index = i; + let changeBut = document.createElement("button"); changeBut.className = "btn btn-success"; wasReadCell.appendChild(changeBut); let readStatus = myLibrary[i].check ? "Read" : "Not Read"; changeBut.innerText = readStatus; - +const index = i; changeBut.addEventListener("click", function () { myLibrary[index].check = !myLibrary[index].check; render(); From 910967445d879662e4208e30f4c08763f4eb59c8 Mon Sep 17 00:00:00 2001 From: Mona-Eltantawy Date: Wed, 22 Apr 2026 12:47:45 +0100 Subject: [PATCH 6/9] fixed the render loop --- debugging/book-library/script.js | 105 +++++++++++++++---------------- 1 file changed, 52 insertions(+), 53 deletions(-) diff --git a/debugging/book-library/script.js b/debugging/book-library/script.js index cb607cff..55654c13 100644 --- a/debugging/book-library/script.js +++ b/debugging/book-library/script.js @@ -2,20 +2,19 @@ let myLibrary = []; window.addEventListener("load", function () { populateStorage(); - }); function populateStorage() { - if (myLibrary.length == 0) { - let book1 = new Book("Robison Crusoe", "Daniel Defoe", "252", true); + if (myLibrary.length === 0) { + let book1 = new Book("Robinson Crusoe", "Daniel Defoe", "252", true); let book2 = new Book( "The Old Man and the Sea", "Ernest Hemingway", "127", true ); - myLibrary.push(book1); - myLibrary.push(book2); + + myLibrary.push(book1, book2); render(); } } @@ -25,24 +24,28 @@ const author = document.getElementById("author"); const pages = document.getElementById("pages"); const check = document.getElementById("check"); -//check the right input from forms and if its ok -> add the new book (object in array) -//via Book function and start render function +// Add book function submit() { - if ( - title.value = ""; -author.value = ""; -pages.value = ""; -check.checked = false; - ) { + // ✅ Validation + if (!title.value || !author.value || !pages.value) { alert("Please fill all fields!"); - return false; - } else { - let book = new Book(title.value, author.value, pages.value, check.checked); - myLibrary.push(book); - render(); + return; } + + // ✅ Create and store book + let book = new Book(title.value, author.value, pages.value, check.checked); + myLibrary.push(book); + + // ✅ Reset form + title.value = ""; + author.value = ""; + pages.value = ""; + check.checked = false; + + render(); } +// Book constructor function Book(title, author, pages, check) { this.title = title; this.author = author; @@ -50,55 +53,51 @@ function Book(title, author, pages, check) { this.check = check; } +// Render table function render() { let table = document.getElementById("display"); - let rowsNumber = table.rows.length; - //delete old table - for (let n = rowsNumber - 1; n > 0; n--) { - table.deleteRow(n); + + // ✅ Clear old rows (keep header) + for (let i = table.rows.length - 1; i > 0; i--) { + table.deleteRow(i); } - //insert updated row and cells - let length = myLibrary.length; - for (let i = 0; i < length; i++) { + + // ✅ Add updated rows + for (let i = 0; i < myLibrary.length; i++) { let row = table.insertRow(); + let titleCell = row.insertCell(0); let authorCell = row.insertCell(1); let pagesCell = row.insertCell(2); let wasReadCell = row.insertCell(3); let deleteCell = row.insertCell(4); - titleCell.innerHTML = myLibrary[i].title; - authorCell.innerHTML = myLibrary[i].author; - pagesCell.innerHTML = myLibrary[i].pages; - - //add and wait for action for read/unread button - -let changeBut = document.createElement("button"); -changeBut.className = "btn btn-success"; -wasReadCell.appendChild(changeBut); - -let readStatus = myLibrary[i].check ? "Read" : "Not Read"; -changeBut.innerText = readStatus; -const index = i; -changeBut.addEventListener("click", function () { - myLibrary[index].check = !myLibrary[index].check; - render(); -}); -delButton.addEventListener("click", function () { - alert(`You've deleted title: ${myLibrary[index].title}`); - myLibrary.splice(index, 1); - render(); -}); + titleCell.innerText = myLibrary[i].title; + authorCell.innerText = myLibrary[i].author; + pagesCell.innerText = myLibrary[i].pages; + + const index = i; + + // ✅ Read toggle button + let changeBut = document.createElement("button"); + changeBut.className = "btn btn-success"; + changeBut.innerText = myLibrary[i].check ? "Read" : "Not Read"; + wasReadCell.appendChild(changeBut); - //add delete button to every row and render again + changeBut.addEventListener("click", function () { + myLibrary[index].check = !myLibrary[index].check; + render(); + }); + + // ✅ Delete button let delButton = document.createElement("button"); - delButton.id = i + 5; - deleteCell.appendChild(delButton); delButton.className = "btn btn-warning"; - delButton.innerHTML = "Delete"; + delButton.innerText = "Delete"; + deleteCell.appendChild(delButton); + delButton.addEventListener("click", function () { - alert(`You've deleted title: ${myLibrary[i].title}`); - myLibrary.splice(i, 1); + alert(`You've deleted title: ${myLibrary[index].title}`); + myLibrary.splice(index, 1); render(); }); } From bc9b6949e8a3a9d16a22aa8ee2b26361ccb84bbc Mon Sep 17 00:00:00 2001 From: Mona-Eltantawy Date: Fri, 24 Apr 2026 08:24:36 +0100 Subject: [PATCH 7/9] Refactor: Improve code quality and input validation in book library app - Remove unnecessary empty placeholder row from table - Add consistent 'El' suffix to all DOM node variable names for clarity - Improve input validation with explicit NaN check and page count range validation (1-9999) - Update error message to reflect validation constraints --- debugging/book-library/index.html | 47 +++++++-------- debugging/book-library/script.js | 98 ++++++++++++++++++------------- debugging/book-library/style.css | 20 +++++-- 3 files changed, 92 insertions(+), 73 deletions(-) diff --git a/debugging/book-library/index.html b/debugging/book-library/index.html index 23acfa71..031bfb2a 100644 --- a/debugging/book-library/index.html +++ b/debugging/book-library/index.html @@ -1,20 +1,14 @@ - + - - + Book Library + + - - + + @@ -31,20 +25,24 @@

Library

+ minlength="2" + maxlength="100" + > + minlength="2" + maxlength="100" + > Library id="pages" name="pages" required - /> + min="1" + max="9999" + > + >
@@ -81,16 +81,9 @@

Library

- - - - - - - - + diff --git a/debugging/book-library/script.js b/debugging/book-library/script.js index 55654c13..1af3ae6c 100644 --- a/debugging/book-library/script.js +++ b/debugging/book-library/script.js @@ -6,11 +6,11 @@ window.addEventListener("load", function () { function populateStorage() { if (myLibrary.length === 0) { - let book1 = new Book("Robinson Crusoe", "Daniel Defoe", "252", true); + let book1 = new Book("Robinson Crusoe", "Daniel Defoe", 252, true); let book2 = new Book( "The Old Man and the Sea", "Ernest Hemingway", - "127", + 127, true ); @@ -19,83 +19,97 @@ function populateStorage() { } } -const title = document.getElementById("title"); -const author = document.getElementById("author"); -const pages = document.getElementById("pages"); -const check = document.getElementById("check"); +const titleInputEl = document.getElementById("title"); +const authorInputEl = document.getElementById("author"); +const pagesInputEl = document.getElementById("pages"); +const checkInputEl = document.getElementById("check"); // Add book function submit() { - // ✅ Validation - if (!title.value || !author.value || !pages.value) { - alert("Please fill all fields!"); + // ✅ Input Preprocessing + // Sanitization: trim whitespace from text inputs + const trimmedTitle = titleInputEl.value.trim(); + const trimmedAuthor = authorInputEl.value.trim(); + const pages = Number(pagesInputEl.value); + + // ✅ Input Validation + // Reject: empty/whitespace-only strings, non-numeric page input, non-positive counts, or values exceeding max + if ( + !trimmedTitle || + !trimmedAuthor || + isNaN(pages) || + pages < 1 || + pages > 9999 + ) { + alert( + "Please provide valid input: non-empty title/author and page count between 1-9999!" + ); return; } - // ✅ Create and store book - let book = new Book(title.value, author.value, pages.value, check.checked); + // ✅ Create and store book with sanitized/validated input + let book = new Book(trimmedTitle, trimmedAuthor, pages, checkInputEl.checked); myLibrary.push(book); // ✅ Reset form - title.value = ""; - author.value = ""; - pages.value = ""; - check.checked = false; + titleInputEl.value = ""; + authorInputEl.value = ""; + pagesInputEl.value = ""; + checkInputEl.checked = false; render(); } // Book constructor -function Book(title, author, pages, check) { +function Book(title, author, pages, isRead) { this.title = title; this.author = author; this.pages = pages; - this.check = check; + this.isRead = isRead; } // Render table function render() { - let table = document.getElementById("display"); + let displayTableEl = document.getElementById("display"); + let tbodyEl = displayTableEl.querySelector("tbody"); - // ✅ Clear old rows (keep header) - for (let i = table.rows.length - 1; i > 0; i--) { - table.deleteRow(i); - } + // ✅ Clear old rows in one operation (keep header) + tbodyEl.innerHTML = ""; // ✅ Add updated rows for (let i = 0; i < myLibrary.length; i++) { - let row = table.insertRow(); + let rowEl = displayTableEl.insertRow(); - let titleCell = row.insertCell(0); - let authorCell = row.insertCell(1); - let pagesCell = row.insertCell(2); - let wasReadCell = row.insertCell(3); - let deleteCell = row.insertCell(4); + let titleCellEl = rowEl.insertCell(0); + let authorCellEl = rowEl.insertCell(1); + let pagesCellEl = rowEl.insertCell(2); + let wasReadCellEl = rowEl.insertCell(3); + let deleteCellEl = rowEl.insertCell(4); - titleCell.innerText = myLibrary[i].title; - authorCell.innerText = myLibrary[i].author; - pagesCell.innerText = myLibrary[i].pages; + titleCellEl.innerText = myLibrary[i].title; + authorCellEl.innerText = myLibrary[i].author; + pagesCellEl.innerText = myLibrary[i].pages; const index = i; // ✅ Read toggle button - let changeBut = document.createElement("button"); - changeBut.className = "btn btn-success"; - changeBut.innerText = myLibrary[i].check ? "Read" : "Not Read"; - wasReadCell.appendChild(changeBut); + let readToggleBtnEl = document.createElement("button"); + readToggleBtnEl.className = "btn btn-success"; + readToggleBtnEl.innerText = myLibrary[i].isRead ? "Read" : "Not Read"; + wasReadCellEl.appendChild(readToggleBtnEl); - changeBut.addEventListener("click", function () { - myLibrary[index].check = !myLibrary[index].check; + readToggleBtnEl.addEventListener("click", function () { + myLibrary[index].isRead = !myLibrary[index].isRead; render(); }); // ✅ Delete button - let delButton = document.createElement("button"); - delButton.className = "btn btn-warning"; - delButton.innerText = "Delete"; - deleteCell.appendChild(delButton); + let deleteBtnEl = document.createElement("button"); + deleteBtnEl.className = "btn btn-warning"; + deleteBtnEl.innerText = "Delete"; + deleteCellEl.appendChild(deleteBtnEl); - delButton.addEventListener("click", function () { + deleteBtnEl.addEventListener("click", function () { alert(`You've deleted title: ${myLibrary[index].title}`); myLibrary.splice(index, 1); render(); diff --git a/debugging/book-library/style.css b/debugging/book-library/style.css index 302950cb..a0f5ae32 100644 --- a/debugging/book-library/style.css +++ b/debugging/book-library/style.css @@ -1,19 +1,31 @@ .form-group { - width: 400px; - height: 300px; - align-self: left; + max-width: 400px; padding-left: 20px; + margin-top: 10px; + /* fixed */ } .btn { display: block; } +.btn-primary { + margin-top: 15px; +} + .form-check-label { padding-left: 20px; - margin: 5px 0px 5px 0px; + margin: 5px 0; } button.btn-info { margin: 20px; } + +.jumbotron { + background-color: #7c8b9a; + color: white; + padding:10px 20px; + margin-bottom: 30px; + border-radius: 15px; +} \ No newline at end of file From adaf7fcaf96815a717822c97538e7c0f306c38bb Mon Sep 17 00:00:00 2001 From: Mona-Eltantawy Date: Fri, 24 Apr 2026 12:34:08 +0100 Subject: [PATCH 8/9] Fix book library: improve button naming consistency, remove blocking alerts, add non-blocking notifications --- debugging/book-library/script.js | 36 +++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/debugging/book-library/script.js b/debugging/book-library/script.js index 1af3ae6c..78c94287 100644 --- a/debugging/book-library/script.js +++ b/debugging/book-library/script.js @@ -92,7 +92,7 @@ function render() { const index = i; - // ✅ Read toggle button + // ✅ Read toggle button (no id needed - event listeners handle functionality) let readToggleBtnEl = document.createElement("button"); readToggleBtnEl.className = "btn btn-success"; readToggleBtnEl.innerText = myLibrary[i].isRead ? "Read" : "Not Read"; @@ -103,15 +103,35 @@ function render() { render(); }); - // ✅ Delete button - let deleteBtnEl = document.createElement("button"); - deleteBtnEl.className = "btn btn-warning"; - deleteBtnEl.innerText = "Delete"; - deleteCellEl.appendChild(deleteBtnEl); + // ✅ Delete button (no id needed - event listeners handle functionality) + let deleteButtonEl = document.createElement("button"); + deleteButtonEl.className = "btn btn-warning"; + deleteButtonEl.innerText = "Delete"; + deleteCellEl.appendChild(deleteButtonEl); - deleteBtnEl.addEventListener("click", function () { - alert(`You've deleted title: ${myLibrary[index].title}`); + deleteButtonEl.addEventListener("click", function () { + const title = myLibrary[index].title; myLibrary.splice(index, 1); + + // Show non-blocking notification (doesn't freeze the page) + const notification = document.createElement("div"); + notification.className = "alert alert-info alert-dismissible fade show"; + notification.style.position = "fixed"; + notification.style.top = "20px"; + notification.style.left = "50%"; + notification.style.transform = "translateX(-50%)"; + notification.style.zIndex = "9999"; + notification.innerHTML = `You've deleted title: ${title} + `; + document.body.appendChild(notification); + + // Auto-remove after 4 seconds + setTimeout(() => { + if (notification.parentNode) { + notification.remove(); + } + }, 4000); + render(); }); } From 0ba38768fe4af5c7904831569368c31163609018 Mon Sep 17 00:00:00 2001 From: Mona-Eltantawy Date: Fri, 24 Apr 2026 12:53:33 +0100 Subject: [PATCH 9/9] Refactor book library: update HTML form structure, improve accessibility, and validate with W3C --- debugging/book-library/index.html | 153 +++++++++++------------- debugging/book-library/script.js | 190 ++++++++++++++---------------- debugging/book-library/style.css | 39 +++--- 3 files changed, 185 insertions(+), 197 deletions(-) diff --git a/debugging/book-library/index.html b/debugging/book-library/index.html index 031bfb2a..ed992a81 100644 --- a/debugging/book-library/index.html +++ b/debugging/book-library/index.html @@ -1,89 +1,80 @@ - - Book Library - - - - - - - - - - -
-

Library

-

Add books to your virtual library

-
+ + Book Library + + + + + + + + + + + + + + + +
+

Library

+

Add books to your virtual library

+
+ + +
+
+ + +
+
+ + + + + + -
-
- - - - - - - - + + + +
+ +
-
- - - - - - - - - - - - - -
TitleAuthorNumber of PagesRead
- - - - + + + + + +
+ + +
+ + + + + + + + + + + + + +
TitleAuthorNumber of PagesReadDelete
+ + + + + + \ No newline at end of file diff --git a/debugging/book-library/script.js b/debugging/book-library/script.js index 78c94287..08fb7385 100644 --- a/debugging/book-library/script.js +++ b/debugging/book-library/script.js @@ -1,138 +1,128 @@ -let myLibrary = []; +// ✅ Data +const myLibrary = []; -window.addEventListener("load", function () { - populateStorage(); +// ✅ DOM elements (clear naming) +const formEl = document.getElementById("bookForm"); +const titleInputEl = document.getElementById("title"); +const authorInputEl = document.getElementById("author"); +const pagesInputEl = document.getElementById("pages"); +const checkInputEl = document.getElementById("check"); +const tableBodyEl = document.querySelector("#display tbody"); +const messageEl = document.getElementById("message"); + +// ✅ Init +window.addEventListener("load", () => { + populateStorage(); // only called once + render(); }); +// ✅ Populate initial data function populateStorage() { if (myLibrary.length === 0) { - let book1 = new Book("Robinson Crusoe", "Daniel Defoe", 252, true); - let book2 = new Book( - "The Old Man and the Sea", - "Ernest Hemingway", - 127, - true + myLibrary.push( + new Book("Robinson Crusoe", "Daniel Defoe", 252, true), + new Book("The Old Man and the Sea", "Ernest Hemingway", 127, true) ); - - myLibrary.push(book1, book2); - render(); } } -const titleInputEl = document.getElementById("title"); -const authorInputEl = document.getElementById("author"); -const pagesInputEl = document.getElementById("pages"); -const checkInputEl = document.getElementById("check"); +// ✅ Handle form submit (NO inline onclick) +formEl.addEventListener("submit", (e) => { + e.preventDefault(); -// Add book -function submit() { - // ✅ Input Preprocessing - // Sanitization: trim whitespace from text inputs - const trimmedTitle = titleInputEl.value.trim(); - const trimmedAuthor = authorInputEl.value.trim(); + // 🔹 Preprocessing + const title = titleInputEl.value.trim(); + const author = authorInputEl.value.trim(); const pages = Number(pagesInputEl.value); + const isRead = checkInputEl.checked; - // ✅ Input Validation - // Reject: empty/whitespace-only strings, non-numeric page input, non-positive counts, or values exceeding max - if ( - !trimmedTitle || - !trimmedAuthor || - isNaN(pages) || - pages < 1 || - pages > 9999 - ) { - alert( - "Please provide valid input: non-empty title/author and page count between 1-9999!" - ); + // 🔹 Validation + if (!title || !author) { + showMessage("Title and Author cannot be empty."); + return; + } + + if (Number.isNaN(pages) || pages < 1 || pages > 9999) { + showMessage("Pages must be between 1 and 9999."); return; } - // ✅ Create and store book with sanitized/validated input - let book = new Book(trimmedTitle, trimmedAuthor, pages, checkInputEl.checked); + // 🔹 Add book + const book = new Book(title, author, pages, isRead); myLibrary.push(book); - // ✅ Reset form - titleInputEl.value = ""; - authorInputEl.value = ""; - pagesInputEl.value = ""; - checkInputEl.checked = false; + // 🔹 Reset form + formEl.reset(); render(); -} +}); -// Book constructor +// ✅ Constructor function Book(title, author, pages, isRead) { this.title = title; this.author = author; - this.pages = pages; + this.pages = pages; // number (correct type) this.isRead = isRead; } -// Render table +// ✅ Render table function render() { - let displayTableEl = document.getElementById("display"); - let tbodyEl = displayTableEl.querySelector("tbody"); - - // ✅ Clear old rows in one operation (keep header) - tbodyEl.innerHTML = ""; - - // ✅ Add updated rows - for (let i = 0; i < myLibrary.length; i++) { - let rowEl = displayTableEl.insertRow(); - - let titleCellEl = rowEl.insertCell(0); - let authorCellEl = rowEl.insertCell(1); - let pagesCellEl = rowEl.insertCell(2); - let wasReadCellEl = rowEl.insertCell(3); - let deleteCellEl = rowEl.insertCell(4); + // 🔹 Efficient clear + tableBodyEl.innerHTML = ""; + + myLibrary.forEach((book, index) => { + const row = document.createElement("tr"); + + const titleCell = document.createElement("td"); + const authorCell = document.createElement("td"); + const pagesCell = document.createElement("td"); + const readCell = document.createElement("td"); + const deleteCell = document.createElement("td"); + + // 🔹 Safe text assignment + titleCell.textContent = book.title; + authorCell.textContent = book.author; + pagesCell.textContent = book.pages; + + // 🔹 Toggle read button (simplified) + const toggleBtn = document.createElement("button"); + toggleBtn.className = "btn btn-success"; + toggleBtn.textContent = book.isRead ? "Yes" : "No"; + + toggleBtn.addEventListener("click", () => { + book.isRead = !book.isRead; + render(); + }); - titleCellEl.innerText = myLibrary[i].title; - authorCellEl.innerText = myLibrary[i].author; - pagesCellEl.innerText = myLibrary[i].pages; + readCell.appendChild(toggleBtn); - const index = i; + // 🔹 Delete button + const deleteBtn = document.createElement("button"); + deleteBtn.className = "btn btn-warning"; + deleteBtn.textContent = "Delete"; - // ✅ Read toggle button (no id needed - event listeners handle functionality) - let readToggleBtnEl = document.createElement("button"); - readToggleBtnEl.className = "btn btn-success"; - readToggleBtnEl.innerText = myLibrary[i].isRead ? "Read" : "Not Read"; - wasReadCellEl.appendChild(readToggleBtnEl); + deleteBtn.addEventListener("click", () => { + // delete first + const deletedTitle = book.title; + myLibrary.splice(index, 1); - readToggleBtnEl.addEventListener("click", function () { - myLibrary[index].isRead = !myLibrary[index].isRead; + // then show message (non-blocking) + showMessage(`Deleted: "${deletedTitle}"`); render(); }); - // ✅ Delete button (no id needed - event listeners handle functionality) - let deleteButtonEl = document.createElement("button"); - deleteButtonEl.className = "btn btn-warning"; - deleteButtonEl.innerText = "Delete"; - deleteCellEl.appendChild(deleteButtonEl); + deleteCell.appendChild(deleteBtn); - deleteButtonEl.addEventListener("click", function () { - const title = myLibrary[index].title; - myLibrary.splice(index, 1); + row.append(titleCell, authorCell, pagesCell, readCell, deleteCell); + tableBodyEl.appendChild(row); + }); +} - // Show non-blocking notification (doesn't freeze the page) - const notification = document.createElement("div"); - notification.className = "alert alert-info alert-dismissible fade show"; - notification.style.position = "fixed"; - notification.style.top = "20px"; - notification.style.left = "50%"; - notification.style.transform = "translateX(-50%)"; - notification.style.zIndex = "9999"; - notification.innerHTML = `You've deleted title: ${title} - `; - document.body.appendChild(notification); - - // Auto-remove after 4 seconds - setTimeout(() => { - if (notification.parentNode) { - notification.remove(); - } - }, 4000); +// ✅ Non-blocking message (instead of alert) +function showMessage(text) { + messageEl.textContent = text; - render(); - }); - } + setTimeout(() => { + messageEl.textContent = ""; + }, 2000); } diff --git a/debugging/book-library/style.css b/debugging/book-library/style.css index a0f5ae32..f21c6b9f 100644 --- a/debugging/book-library/style.css +++ b/debugging/book-library/style.css @@ -1,31 +1,38 @@ +/* Form container */ .form-group { max-width: 400px; - padding-left: 20px; - margin-top: 10px; - /* fixed */ + /* responsive instead of fixed width */ + margin: 20px auto; + /* center horizontally */ + padding: 20px; } +/* Buttons */ .btn { - display: block; + display: inline-block; + /* don't force all buttons to stack */ + margin-top: 10px; } -.btn-primary { - margin-top: 15px; +/* Collapse toggle button */ +button.btn-info { + margin: 20px auto; + display: block; } +/* Checkbox label spacing */ .form-check-label { - padding-left: 20px; - margin: 5px 0; + margin-left: 5px; } -button.btn-info { - margin: 20px; +/* Table spacing */ +.table { + margin-top: 30px; } -.jumbotron { - background-color: #7c8b9a; - color: white; - padding:10px 20px; - margin-bottom: 30px; - border-radius: 15px; +/* Message styling */ +#message { + text-align: center; + font-weight: bold; + margin-top: 10px; } \ No newline at end of file