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
67const titleInput = document . getElementById ( "title" ) ;
78const authorInput = document . getElementById ( "author" ) ;
89const pagesInput = document . getElementById ( "pages" ) ;
910const 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
1413window . 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
2119function 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
3230function 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
4259function 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
7490function 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
108120function 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