Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 53 additions & 72 deletions debugging/book-library/index.html
Original file line number Diff line number Diff line change
@@ -1,73 +1,60 @@
<!DOCTYPE html>
<html>
<head>
<title> </title>
<meta
charset="utf-8"
name="viewport"
content="width=device-width, initial-scale=1.0"
/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script>
<link
rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
/>
<link rel="stylesheet" type="text/css" href="style.css" />
</head>
<html lang="en">

<body>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Virtual Library</title>

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" href="style.css" />

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script>

<!-- ES Module script tag -->
<script src="script.js" type="module"></script>
</head>

<body>
<div class="container my-4">
<div class="jumbotron text-center">
<h1>Library</h1>
<p>Add books to your virtual library</p>
</div>

<button data-toggle="collapse" data-target="#demo" class="btn btn-info">
<!-- Feedback container for delete notifications without window.alert() -->
<div id="status-message" class="alert alert-info d-none" role="alert"></div>

<button data-toggle="collapse" data-target="#demo" class="btn btn-info mb-3">
Add new book
</button>

<div id="demo" class="collapse">
<div class="form-group">
<label for="title">Title:</label>
<input
type="title"
class="form-control"
id="title"
name="title"
required
/>
<label for="author">Author: </label>
<input
type="author"
class="form-control"
id="author"
name="author"
required
/>
<label for="pages">Pages:</label>
<input
type="number"
class="form-control"
id="pages"
name="pages"
required
/>
<label class="form-check-label">
<input
type="checkbox"
class="form-check-input"
id="check"
value=""
/>Read
</label>
<input
type="submit"
value="Submit"
class="btn btn-primary"
onclick="submit();"
/>
</div>
<div id="demo" class="collapse mb-4">
<form id="book-form">
<div class="form-group">
<label for="title">Title:</label>
<input type="text" class="form-control" id="title" required />
</div>

<div class="form-group">
<label for="author">Author:</label>
<input type="text" class="form-control" id="author" required />
</div>

<div class="form-group">
<label for="pages">Pages:</label>
<input type="number" class="form-control" id="pages" min="1" required />
</div>
Comment on lines +46 to +49

@cjyuan cjyuan Aug 5, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not make this input element to accept only whole number?


<div class="form-group form-check">
<input type="checkbox" class="form-check-input" id="check" />
<label class="form-check-label" for="check">Read</label>
</div>

<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>

<table class="table" id="display">
Expand All @@ -77,20 +64,14 @@ <h1>Library</h1>
<th>Author</th>
<th>Number of Pages</th>
<th>Read</th>
<th></th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tbody id="book-list">
<!-- Dynamically populated -->
</tbody>
</table>
</div>
</body>

<script src="script.js"></script>
</body>
</html>
</html>
199 changes: 112 additions & 87 deletions debugging/book-library/script.js
Original file line number Diff line number Diff line change
@@ -1,103 +1,128 @@
let myLibrary = [];
// Data store
const myLibrary = [];

window.addEventListener("load", function (e) {
populateStorage();
render();
});

function populateStorage() {
if (myLibrary.length == 0) {
let book1 = new Book("Robison Crusoe", "Daniel Defoe", "252", true);
let book2 = new Book(
"The Old Man and the Sea",
"Ernest Hemingway",
"127",
true
// DOM Element References with clear suffixes
const bookForm = document.getElementById("book-form");
const titleInput = document.getElementById("title");
const authorInput = document.getElementById("author");
const pagesInput = document.getElementById("pages");
const checkInput = document.getElementById("check");
const bookListBody = document.getElementById("book-list");
const statusMessageEl = document.getElementById("status-message");

// Book Constructor
function Book(title, author, pages, check) {
this.title = title;
this.author = author;
this.pages = pages; // Stored as Number
this.check = check; // Stored as Boolean
}

// Populate default books once on load
function initLibrary() {
if (myLibrary.length === 0) {
myLibrary.push(new Book("Robinson Crusoe", "Daniel Defoe", 252, true));
myLibrary.push(
new Book("The Old Man and the Sea", "Ernest Hemingway", 127, true)
);
myLibrary.push(book1);
myLibrary.push(book2);
render();
}
render();
}

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
function submit() {
if (
title.value == null ||
title.value == "" ||
pages.value == null ||
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();
// Preprocess and handle form submission
function handleFormSubmit(event) {
event.preventDefault();

// Input Sanitization & Normalization (.trim() and Number conversion)
const titleValue = titleInput.value.trim();
const authorValue = authorInput.value.trim();
const pagesValue = Number(pagesInput.value);
const isRead = checkInput.checked;

// Validation check
if (!titleValue || !authorValue || isNaN(pagesValue) || pagesValue <= 0) {
showStatus("Please enter valid book details.", "danger");
return;
}
Comment on lines +39 to 46

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if pageValue is a number like 3.1416?

}

function Book(title, author, pages, check) {
this.title = title;
this.author = author;
this.pages = pages;
this.check = check;
// Create and add new book
const newBook = new Book(titleValue, authorValue, pagesValue, isRead);
myLibrary.push(newBook);

// Reset form inputs
bookForm.reset();

// Render updated table
render();
}

// Efficient DOM Rendering
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;

//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;

changeBut.addEventListener("click", function () {
myLibrary[i].check = !myLibrary[i].check;
// Fast clearing of table rows without loops
bookListBody.textContent = "";

myLibrary.forEach((book, index) => {
const row = document.createElement("tr");

const titleCell = document.createElement("td");
titleCell.textContent = book.title; // Safe textContent preventing XSS

const authorCell = document.createElement("td");
authorCell.textContent = book.author;

const pagesCell = document.createElement("td");
pagesCell.textContent = book.pages;

// Toggle Read Button
const wasReadCell = document.createElement("td");
const toggleReadBtn = document.createElement("button");
toggleReadBtn.className = book.check
? "btn btn-success btn-sm"
: "btn btn-secondary btn-sm";
toggleReadBtn.textContent = book.check ? "Yes" : "No"; // Concise ternary operator

toggleReadBtn.addEventListener("click", () => {
book.check = !book.check;
render();
});
wasReadCell.appendChild(toggleReadBtn);

// Delete Button
const deleteCell = document.createElement("td");
const deleteBtn = document.createElement("button");
deleteBtn.className = "btn btn-danger btn-sm";
deleteBtn.textContent = "Delete";

//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);
deleteBtn.addEventListener("click", () => {
const deletedTitle = book.title;

// Perform deletion FIRST
myLibrary.splice(index, 1);
render();

// Display non-blocking feedback AFTER deletion
showStatus(`You deleted "${deletedTitle}" from your library.`, "info");
});
}
deleteCell.appendChild(deleteBtn);

// Append cells to row
row.append(titleCell, authorCell, pagesCell, wasReadCell, deleteCell);
bookListBody.appendChild(row);
});
}

// Non-blocking status message UI helper
function showStatus(message, type = "info") {
statusMessageEl.textContent = message;
statusMessageEl.className = `alert alert-${type}`;
statusMessageEl.classList.remove("d-none");

// Auto-hide after 3 seconds
setTimeout(() => {
statusMessageEl.classList.add("d-none");
}, 3000);
}

// Event Listeners (Module compatible)
bookForm.addEventListener("submit", handleFormSubmit);
window.addEventListener("DOMContentLoaded", initLibrary);
12 changes: 6 additions & 6 deletions debugging/book-library/style.css
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
.form-group {
width: 400px;
height: 300px;
align-self: left;
max width: 400px;
height: 100px;
margin-bottom: 12px;
padding-left: 20px;
}

.btn {
display: block;
}
button.btn-info {
margin: 20px;
}

.form-check-label {
padding-left: 20px;
margin: 5px 0px 5px 0px;
}

button.btn-info {
margin: 20px;
}
Loading