11const myLibrary = [ ] ;
22
3-
3+ const bookForm = document . getElementById ( "bookForm" ) ;
44const titleInput = document . getElementById ( "title" ) ;
55const authorInput = document . getElementById ( "author" ) ;
66const pagesInput = document . getElementById ( "pages" ) ;
@@ -9,12 +9,11 @@ const checkInput = document.getElementById("check");
99function Book ( title , author , pages , read ) {
1010 this . title = title ;
1111 this . author = author ;
12- this . pages = pages ; // stored as a Number now, not a trimmed string
12+ this . pages = pages ;
1313 this . read = ! ! read ;
1414}
1515
1616function populateStorage ( ) {
17- // Seed with a couple of books if library is empty
1817 if ( myLibrary . length === 0 ) {
1918 const book1 = new Book ( "Robinson Crusoe" , "Daniel Defoe" , 252 , true ) ;
2019 const book2 = new Book (
@@ -27,37 +26,24 @@ function populateStorage() {
2726 }
2827}
2928
30- // Check the right input from forms and if it's ok -> add the new book (object in array)
31- // via Book function and start render function
3229function addBook ( ) {
3330 const title = titleInput . value . trim ( ) ;
3431 const author = authorInput . value . trim ( ) ;
3532 const pagesRaw = pagesInput . value . trim ( ) ;
3633 const pages = Number ( pagesRaw ) ;
3734
38- // Preprocessing / validation:
39- // - reject empty or whitespace-only title/author (checked AFTER trim, not before)
40- // - reject non-numeric or non-positive page counts
41- if (
42- ! title ||
43- ! author ||
44- ! Number . isInteger ( pages ) ||
45- pages <= 0
46- ) {
47- // invalid
48- }
35+ // Native "required"/"min"/"step" already ran before this handler fires
36+ // (see bookForm's submit listener below), so this is a second layer
37+ // for things HTML attributes can't express, like whitespace-only text.
38+ if ( ! title || ! author || ! pagesRaw || ! Number . isFinite ( pages ) || pages <= 0 ) {
4939 alert ( "Please fill all fields with valid values!" ) ;
5040 return false ;
5141 }
5242
5343 const book = new Book ( title , author , pages , checkInput . checked ) ;
5444 myLibrary . push ( book ) ;
5545
56- // clear form inputs
57- titleInput . value = "" ;
58- authorInput . value = "" ;
59- pagesInput . value = "" ;
60- checkInput . checked = false ;
46+ bookForm . reset ( ) ;
6147
6248 render ( ) ;
6349 return true ;
@@ -67,14 +53,13 @@ function render() {
6753 const table = document . getElementById ( "display" ) ;
6854 const tbody = table . getElementsByTagName ( "tbody" ) [ 0 ] ;
6955
70- // Clear existing rows in one operation instead of removing one at a time
7156 tbody . innerHTML = "" ;
7257
7358 myLibrary . forEach ( ( book ) => {
7459 const row = document . createElement ( "tr" ) ;
7560
7661 const titleCell = document . createElement ( "td" ) ;
77- titleCell . textContent = book . title ; // textContent: safe, no HTML parsing needed here
62+ titleCell . textContent = book . title ;
7863 row . appendChild ( titleCell ) ;
7964
8065 const authorCell = document . createElement ( "td" ) ;
@@ -86,7 +71,7 @@ function render() {
8671 row . appendChild ( pagesCell ) ;
8772
8873 const readCell = document . createElement ( "td" ) ;
89- const toggleReadBtn = document . createElement ( "button" ) ; // consistent "Btn" suffix
74+ const toggleReadBtn = document . createElement ( "button" ) ;
9075 toggleReadBtn . className = "btn btn-sm btn-outline-primary" ;
9176 toggleReadBtn . textContent = book . read ? "Yes" : "No" ;
9277 toggleReadBtn . addEventListener ( "click" , ( ) => {
@@ -97,20 +82,15 @@ function render() {
9782 row . appendChild ( readCell ) ;
9883
9984 const deleteCell = document . createElement ( "td" ) ;
100- const deleteBtn = document . createElement ( "button" ) ; // consistent "Btn" suffix
85+ const deleteBtn = document . createElement ( "button" ) ;
10186 deleteBtn . className = "btn btn-sm btn-danger" ;
10287 deleteBtn . textContent = "Delete" ;
10388 deleteBtn . addEventListener ( "click" , ( ) => {
104- // Look up by object identity, not by closure-captured index,
105- // so this stays correct even if myLibrary is ever reordered/filtered
106- // by something other than a full render().
10789 const idx = myLibrary . indexOf ( book ) ;
10890 if ( idx === - 1 ) return ;
10991
11092 myLibrary . splice ( idx , 1 ) ;
11193 render ( ) ;
112- // Show confirmation only AFTER the delete has actually completed,
113- // and without a blocking window.alert().
11494 showToast ( `Deleted "${ book . title } "` ) ;
11595 } ) ;
11696 deleteCell . appendChild ( deleteBtn ) ;
@@ -120,11 +100,9 @@ function render() {
120100 } ) ;
121101}
122102
123-
124103function showToast ( message , duration = 2000 ) {
125104 const toast = document . getElementById ( "toast" ) ;
126105 if ( ! toast ) {
127- // Fallback if no toast container exists in the page yet.
128106 console . log ( message ) ;
129107 return ;
130108 }
@@ -136,9 +114,16 @@ function showToast(message, duration = 2000) {
136114 } , duration ) ;
137115}
138116
139- // Single init call on page load — NOT called a second time elsewhere.
117+ bookForm . addEventListener ( "submit" , ( event ) => {
118+ event . preventDefault ( ) ;
119+ addBook ( ) ;
120+ } ) ;
121+
140122document . addEventListener ( "DOMContentLoaded" , ( ) => {
141123 populateStorage ( ) ;
142124 render ( ) ;
143- document . getElementById ( "submitBtn" ) . addEventListener ( "click" , addBook ) ;
144125} ) ;
126+
127+
128+
129+
0 commit comments