From e978b0b1baff91a23c36011251525243a5178bdb Mon Sep 17 00:00:00 2001 From: Zadri Abdule Date: Thu, 6 Aug 2026 17:58:53 +0100 Subject: [PATCH 1/9] correct form input types and submit wiring --- debugging/book-library/index.html | 10 +-- debugging/book-library/script.js | 128 +++++++++++++++--------------- 2 files changed, 71 insertions(+), 67 deletions(-) diff --git a/debugging/book-library/index.html b/debugging/book-library/index.html index 23acfa71..a2060ad2 100644 --- a/debugging/book-library/index.html +++ b/debugging/book-library/index.html @@ -1,4 +1,4 @@ - + @@ -31,7 +31,7 @@

Library

Library /> Library />Read
diff --git a/debugging/book-library/script.js b/debugging/book-library/script.js index 75ce6c1d..db663ee1 100644 --- a/debugging/book-library/script.js +++ b/debugging/book-library/script.js @@ -6,17 +6,16 @@ window.addEventListener("load", function (e) { }); function populateStorage() { - if (myLibrary.length == 0) { - let book1 = new Book("Robison Crusoe", "Daniel Defoe", "252", true); - let book2 = new Book( + // Seed with a couple of books if library is empty + if (myLibrary.length === 0) { + const book1 = new Book("Robinson Crusoe", "Daniel Defoe", "252", true); + const book2 = new Book( "The Old Man and the Sea", "Ernest Hemingway", "127", true ); - myLibrary.push(book1); - myLibrary.push(book2); - render(); + myLibrary.push(book1, book2); } } @@ -27,77 +26,82 @@ 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 -function submit() { - if ( - title.value == null || - title.value == "" || - pages.value == null || - pages.value == "" - ) { +function addBook() { + // basic validation + if (!title.value || !author.value || !pages.value) { alert("Please fill all fields!"); return false; - } else { - let book = new Book(title.value, title.value, pages.value, check.checked); - library.push(book); - render(); } + + const book = new Book( + title.value.trim(), + author.value.trim(), + pages.value.trim(), + check.checked + ); + myLibrary.push(book); + // clear form inputs + title.value = ""; + author.value = ""; + pages.value = ""; + check.checked = false; + render(); + return true; } -function Book(title, author, pages, check) { +function Book(title, author, pages, read) { this.title = title; this.author = author; this.pages = pages; - this.check = check; + // store as boolean under a descriptive property + this.read = !!read; } 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); - } - //insert updated row and cells - let length = myLibrary.length; - for (let i = 0; i < length; i++) { - let row = table.insertRow(1); - 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; + const table = document.getElementById("display"); + const tbody = table.getElementsByTagName("tbody")[0]; + // clear existing rows in tbody + while (tbody.firstChild) tbody.removeChild(tbody.firstChild); - //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; + myLibrary.forEach((book, i) => { + const row = document.createElement("tr"); + + const titleCell = document.createElement("td"); + titleCell.textContent = book.title; + row.appendChild(titleCell); + + const authorCell = document.createElement("td"); + authorCell.textContent = book.author; + row.appendChild(authorCell); + + const pagesCell = document.createElement("td"); + pagesCell.textContent = book.pages; + row.appendChild(pagesCell); - changeBut.addEventListener("click", function () { - myLibrary[i].check = !myLibrary[i].check; + const wasReadCell = document.createElement("td"); + const changeBut = document.createElement("button"); + changeBut.className = "btn btn-sm btn-outline-primary"; + changeBut.textContent = book.read ? "Yes" : "No"; + changeBut.addEventListener("click", () => { + book.read = !book.read; render(); }); + wasReadCell.appendChild(changeBut); + row.appendChild(wasReadCell); - //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 () { - alert(`You've deleted title: ${myLibrary[i].title}`); - myLibrary.splice(i, 1); - render(); + const deleteCell = document.createElement("td"); + const delBut = document.createElement("button"); + delBut.className = "btn btn-sm btn-danger"; + delBut.textContent = "Delete"; + delBut.addEventListener("click", () => { + if (confirm(`Delete "${book.title}"?`)) { + myLibrary.splice(i, 1); + render(); + } }); - } + deleteCell.appendChild(delBut); + row.appendChild(deleteCell); + + tbody.appendChild(row); + }); } From 311409497ce66669ae0bae82dc6504bbe8822f72 Mon Sep 17 00:00:00 2001 From: Sophia Date: Thu, 13 Aug 2026 20:30:02 +0100 Subject: [PATCH 2/9] Update script.js --- debugging/book-library/script.js | 138 +++++++++++++++++++------------ 1 file changed, 84 insertions(+), 54 deletions(-) diff --git a/debugging/book-library/script.js b/debugging/book-library/script.js index db663ee1..6790001f 100644 --- a/debugging/book-library/script.js +++ b/debugging/book-library/script.js @@ -1,73 +1,73 @@ let myLibrary = []; -window.addEventListener("load", function (e) { - populateStorage(); - render(); -}); + +const titleInput = document.getElementById("title"); +const authorInput = document.getElementById("author"); +const pagesInput = document.getElementById("pages"); +const checkInput = document.getElementById("check"); + +function Book(title, author, pages, read) { + this.title = title; + this.author = author; + this.pages = pages; // stored as a Number now, not a trimmed string + this.read = !!read; +} function populateStorage() { // Seed with a couple of books if library is empty if (myLibrary.length === 0) { - const book1 = new Book("Robinson Crusoe", "Daniel Defoe", "252", true); + const book1 = new Book("Robinson Crusoe", "Daniel Defoe", 252, true); const book2 = new Book( "The Old Man and the Sea", "Ernest Hemingway", - "127", + 127, true ); myLibrary.push(book1, book2); } } -const title = document.getElementById("title"); -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 +// Check the right input from forms and if it's ok -> add the new book (object in array) +// via Book function and start render function function addBook() { - // basic validation - if (!title.value || !author.value || !pages.value) { - alert("Please fill all fields!"); + const title = titleInput.value.trim(); + const author = authorInput.value.trim(); + const pagesRaw = pagesInput.value.trim(); + const pages = Number(pagesRaw); + + // Preprocessing / validation: + // - reject empty or whitespace-only title/author (checked AFTER trim, not before) + // - reject non-numeric or non-positive page counts + if (!title || !author || !pagesRaw || !Number.isFinite(pages) || pages <= 0) { + alert("Please fill all fields with valid values!"); return false; } - const book = new Book( - title.value.trim(), - author.value.trim(), - pages.value.trim(), - check.checked - ); + const book = new Book(title, author, pages, checkInput.checked); myLibrary.push(book); + // clear form inputs - title.value = ""; - author.value = ""; - pages.value = ""; - check.checked = false; + titleInput.value = ""; + authorInput.value = ""; + pagesInput.value = ""; + checkInput.checked = false; + render(); return true; } -function Book(title, author, pages, read) { - this.title = title; - this.author = author; - this.pages = pages; - // store as boolean under a descriptive property - this.read = !!read; -} - function render() { const table = document.getElementById("display"); const tbody = table.getElementsByTagName("tbody")[0]; - // clear existing rows in tbody - while (tbody.firstChild) tbody.removeChild(tbody.firstChild); - myLibrary.forEach((book, i) => { + // Clear existing rows in one operation instead of removing one at a time + tbody.innerHTML = ""; + + myLibrary.forEach((book) => { const row = document.createElement("tr"); const titleCell = document.createElement("td"); - titleCell.textContent = book.title; + titleCell.textContent = book.title; // textContent: safe, no HTML parsing needed here row.appendChild(titleCell); const authorCell = document.createElement("td"); @@ -78,30 +78,60 @@ function render() { pagesCell.textContent = book.pages; row.appendChild(pagesCell); - const wasReadCell = document.createElement("td"); - const changeBut = document.createElement("button"); - changeBut.className = "btn btn-sm btn-outline-primary"; - changeBut.textContent = book.read ? "Yes" : "No"; - changeBut.addEventListener("click", () => { + const readCell = document.createElement("td"); + const toggleReadBtn = document.createElement("button"); // consistent "Btn" suffix + toggleReadBtn.className = "btn btn-sm btn-outline-primary"; + toggleReadBtn.textContent = book.read ? "Yes" : "No"; + toggleReadBtn.addEventListener("click", () => { book.read = !book.read; render(); }); - wasReadCell.appendChild(changeBut); - row.appendChild(wasReadCell); + readCell.appendChild(toggleReadBtn); + row.appendChild(readCell); const deleteCell = document.createElement("td"); - const delBut = document.createElement("button"); - delBut.className = "btn btn-sm btn-danger"; - delBut.textContent = "Delete"; - delBut.addEventListener("click", () => { - if (confirm(`Delete "${book.title}"?`)) { - myLibrary.splice(i, 1); - render(); - } + const deleteBtn = document.createElement("button"); // consistent "Btn" suffix + deleteBtn.className = "btn btn-sm btn-danger"; + deleteBtn.textContent = "Delete"; + deleteBtn.addEventListener("click", () => { + // Look up by object identity, not by closure-captured index, + // so this stays correct even if myLibrary is ever reordered/filtered + // by something other than a full render(). + const idx = myLibrary.indexOf(book); + if (idx === -1) return; + + myLibrary.splice(idx, 1); + render(); + // Show confirmation only AFTER the delete has actually completed, + // and without a blocking window.alert(). + showToast(`Deleted "${book.title}"`); }); - deleteCell.appendChild(delBut); + deleteCell.appendChild(deleteBtn); row.appendChild(deleteCell); tbody.appendChild(row); }); } + + +function showToast(message, duration = 2000) { + const toast = document.getElementById("toast"); + if (!toast) { + // Fallback if no toast container exists in the page yet. + console.log(message); + return; + } + toast.textContent = message; + toast.classList.add("show"); + clearTimeout(showToast._timer); + showToast._timer = setTimeout(() => { + toast.classList.remove("show"); + }, duration); +} + +// Single init call on page load — NOT called a second time elsewhere. +document.addEventListener("DOMContentLoaded", () => { + populateStorage(); + render(); + document.getElementById("submitBtn").addEventListener("click", addBook); +}); From e5977333f11c394d3db01f184c93845de0cdcd4c Mon Sep 17 00:00:00 2001 From: Sophia Date: Thu, 13 Aug 2026 20:32:24 +0100 Subject: [PATCH 3/9] Update index.html --- debugging/book-library/index.html | 144 +++++++++++++----------------- 1 file changed, 63 insertions(+), 81 deletions(-) diff --git a/debugging/book-library/index.html b/debugging/book-library/index.html index a2060ad2..b9135f09 100644 --- a/debugging/book-library/index.html +++ b/debugging/book-library/index.html @@ -1,96 +1,78 @@ - + - - - - - + + + My Library - + - -
-

Library

-

Add books to your virtual library

-
+
+

My Library

+ +
+
+ + +
- +
+ + +
-
-
- - - - - - - - +
+ + +
+ +
+ + +
+ +
+ +
+ + + + + + + + + + + + +
TitleAuthorPagesReadDelete
- - - - - - - - - - - - - - - - - - - -
TitleAuthorNumber of PagesRead
+
- + From 66eeeacab830fe33036c72126a3aca2972a494dd Mon Sep 17 00:00:00 2001 From: Sophia Date: Sun, 16 Aug 2026 11:56:30 +0100 Subject: [PATCH 4/9] Update script.js --- debugging/book-library/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debugging/book-library/script.js b/debugging/book-library/script.js index 6790001f..0f061c5d 100644 --- a/debugging/book-library/script.js +++ b/debugging/book-library/script.js @@ -1,4 +1,4 @@ -let myLibrary = []; +const myLibrary = []; const titleInput = document.getElementById("title"); From 8a41b5d925d052be1b2134efbbe724f1307d61f3 Mon Sep 17 00:00:00 2001 From: Sophia Date: Sun, 16 Aug 2026 18:51:53 +0100 Subject: [PATCH 5/9] Update index.html --- debugging/book-library/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/debugging/book-library/index.html b/debugging/book-library/index.html index b9135f09..d51479be 100644 --- a/debugging/book-library/index.html +++ b/debugging/book-library/index.html @@ -43,6 +43,7 @@

My Library

+
From 7f5941f82b3ff1967c4e572bd044dc9f77860b00 Mon Sep 17 00:00:00 2001 From: Sophia Date: Mon, 17 Aug 2026 12:50:39 +0100 Subject: [PATCH 6/9] Update index.html --- debugging/book-library/index.html | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/debugging/book-library/index.html b/debugging/book-library/index.html index d51479be..18e6c284 100644 --- a/debugging/book-library/index.html +++ b/debugging/book-library/index.html @@ -9,22 +9,6 @@ href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" /> From ff58ce90491c310e24ed7693b9637e96cbb11c19 Mon Sep 17 00:00:00 2001 From: Sophia Date: Mon, 17 Aug 2026 17:24:42 +0100 Subject: [PATCH 7/9] Update script.js --- debugging/book-library/script.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/debugging/book-library/script.js b/debugging/book-library/script.js index 0f061c5d..5552e256 100644 --- a/debugging/book-library/script.js +++ b/debugging/book-library/script.js @@ -38,7 +38,14 @@ function addBook() { // Preprocessing / validation: // - reject empty or whitespace-only title/author (checked AFTER trim, not before) // - reject non-numeric or non-positive page counts - if (!title || !author || !pagesRaw || !Number.isFinite(pages) || pages <= 0) { + if ( + !title || + !author || + !Number.isInteger(pages) || + pages <= 0 +) { + // invalid +} alert("Please fill all fields with valid values!"); return false; } From 804cd5a086e98cc3ec177be3876111f04c3f1e16 Mon Sep 17 00:00:00 2001 From: Sophia Date: Tue, 18 Aug 2026 22:30:39 +0100 Subject: [PATCH 8/9] Update index.html --- debugging/book-library/index.html | 37 +++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/debugging/book-library/index.html b/debugging/book-library/index.html index 18e6c284..c7fb3f1b 100644 --- a/debugging/book-library/index.html +++ b/debugging/book-library/index.html @@ -9,38 +9,57 @@ href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" />

My Library

-
+
-
-
- - +
-
-
- +
-
+ From 97e0247f3be2f9897031dc74744191dc0010078f Mon Sep 17 00:00:00 2001 From: Sophia Date: Tue, 18 Aug 2026 22:35:12 +0100 Subject: [PATCH 9/9] Update script.js --- debugging/book-library/script.js | 53 ++++++++++++-------------------- 1 file changed, 19 insertions(+), 34 deletions(-) diff --git a/debugging/book-library/script.js b/debugging/book-library/script.js index 5552e256..b252e367 100644 --- a/debugging/book-library/script.js +++ b/debugging/book-library/script.js @@ -1,6 +1,6 @@ const myLibrary = []; - +const bookForm = document.getElementById("bookForm"); const titleInput = document.getElementById("title"); const authorInput = document.getElementById("author"); const pagesInput = document.getElementById("pages"); @@ -9,12 +9,11 @@ const checkInput = document.getElementById("check"); function Book(title, author, pages, read) { this.title = title; this.author = author; - this.pages = pages; // stored as a Number now, not a trimmed string + this.pages = pages; this.read = !!read; } function populateStorage() { - // Seed with a couple of books if library is empty if (myLibrary.length === 0) { const book1 = new Book("Robinson Crusoe", "Daniel Defoe", 252, true); const book2 = new Book( @@ -27,25 +26,16 @@ function populateStorage() { } } -// Check the right input from forms and if it's ok -> add the new book (object in array) -// via Book function and start render function function addBook() { const title = titleInput.value.trim(); const author = authorInput.value.trim(); const pagesRaw = pagesInput.value.trim(); const pages = Number(pagesRaw); - // Preprocessing / validation: - // - reject empty or whitespace-only title/author (checked AFTER trim, not before) - // - reject non-numeric or non-positive page counts - if ( - !title || - !author || - !Number.isInteger(pages) || - pages <= 0 -) { - // invalid -} + // Native "required"/"min"/"step" already ran before this handler fires + // (see bookForm's submit listener below), so this is a second layer + // for things HTML attributes can't express, like whitespace-only text. + if (!title || !author || !pagesRaw || !Number.isFinite(pages) || pages <= 0) { alert("Please fill all fields with valid values!"); return false; } @@ -53,11 +43,7 @@ function addBook() { const book = new Book(title, author, pages, checkInput.checked); myLibrary.push(book); - // clear form inputs - titleInput.value = ""; - authorInput.value = ""; - pagesInput.value = ""; - checkInput.checked = false; + bookForm.reset(); render(); return true; @@ -67,14 +53,13 @@ function render() { const table = document.getElementById("display"); const tbody = table.getElementsByTagName("tbody")[0]; - // Clear existing rows in one operation instead of removing one at a time tbody.innerHTML = ""; myLibrary.forEach((book) => { const row = document.createElement("tr"); const titleCell = document.createElement("td"); - titleCell.textContent = book.title; // textContent: safe, no HTML parsing needed here + titleCell.textContent = book.title; row.appendChild(titleCell); const authorCell = document.createElement("td"); @@ -86,7 +71,7 @@ function render() { row.appendChild(pagesCell); const readCell = document.createElement("td"); - const toggleReadBtn = document.createElement("button"); // consistent "Btn" suffix + const toggleReadBtn = document.createElement("button"); toggleReadBtn.className = "btn btn-sm btn-outline-primary"; toggleReadBtn.textContent = book.read ? "Yes" : "No"; toggleReadBtn.addEventListener("click", () => { @@ -97,20 +82,15 @@ function render() { row.appendChild(readCell); const deleteCell = document.createElement("td"); - const deleteBtn = document.createElement("button"); // consistent "Btn" suffix + const deleteBtn = document.createElement("button"); deleteBtn.className = "btn btn-sm btn-danger"; deleteBtn.textContent = "Delete"; deleteBtn.addEventListener("click", () => { - // Look up by object identity, not by closure-captured index, - // so this stays correct even if myLibrary is ever reordered/filtered - // by something other than a full render(). const idx = myLibrary.indexOf(book); if (idx === -1) return; myLibrary.splice(idx, 1); render(); - // Show confirmation only AFTER the delete has actually completed, - // and without a blocking window.alert(). showToast(`Deleted "${book.title}"`); }); deleteCell.appendChild(deleteBtn); @@ -120,11 +100,9 @@ function render() { }); } - function showToast(message, duration = 2000) { const toast = document.getElementById("toast"); if (!toast) { - // Fallback if no toast container exists in the page yet. console.log(message); return; } @@ -136,9 +114,16 @@ function showToast(message, duration = 2000) { }, duration); } -// Single init call on page load — NOT called a second time elsewhere. +bookForm.addEventListener("submit", (event) => { + event.preventDefault(); + addBook(); +}); + document.addEventListener("DOMContentLoaded", () => { populateStorage(); render(); - document.getElementById("submitBtn").addEventListener("click", addBook); }); + + + +