├── README.md
├── LICENSE
├── index.html
├── .gitignore
└── script
└── script.js
/README.md:
--------------------------------------------------------------------------------
1 | # BookList-JS-NoFrameworks
2 |
3 | 
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Oybek Kayumov
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Document
10 |
11 |
12 |
13 |
14 | MyBook List
15 |
30 |
31 |
32 |
33 | Title
34 | Author
35 | ISBN#
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Test
3 | test/
4 | test.md
5 | test.js
6 |
7 | # Logs
8 | logs
9 | *.log
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 | lerna-debug.log*
14 |
15 | # Diagnostic reports (https://nodejs.org/api/report.html)
16 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
17 |
18 | # Runtime data
19 | pids
20 | *.pid
21 | *.seed
22 | *.pid.lock
23 |
24 | # Directory for instrumented libs generated by jscoverage/JSCover
25 | lib-cov
26 |
27 | # Coverage directory used by tools like istanbul
28 | coverage
29 | *.lcov
30 |
31 | # nyc test coverage
32 | .nyc_output
33 |
34 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
35 | .grunt
36 |
37 | # Bower dependency directory (https://bower.io/)
38 | bower_components
39 |
40 | # node-waf configuration
41 | .lock-wscript
42 |
43 | # Compiled binary addons (https://nodejs.org/api/addons.html)
44 | build/Release
45 |
46 | # Dependency directories
47 | node_modules/
48 | jspm_packages/
49 |
50 | # TypeScript v1 declaration files
51 | typings/
52 |
53 | # TypeScript cache
54 | *.tsbuildinfo
55 |
56 | # Optional npm cache directory
57 | .npm
58 |
59 | # Optional eslint cache
60 | .eslintcache
61 |
62 | # Microbundle cache
63 | .rpt2_cache/
64 | .rts2_cache_cjs/
65 | .rts2_cache_es/
66 | .rts2_cache_umd/
67 |
68 | # Optional REPL history
69 | .node_repl_history
70 |
71 | # Output of 'npm pack'
72 | *.tgz
73 |
74 | # Yarn Integrity file
75 | .yarn-integrity
76 |
77 | # dotenv environment variables file
78 | .env
79 | .env.test
80 |
81 | # parcel-bundler cache (https://parceljs.org/)
82 | .cache
83 |
84 | # Next.js build output
85 | .next
86 |
87 | # Nuxt.js build / generate output
88 | .nuxt
89 | dist
90 |
91 | # Gatsby files
92 | .cache/
93 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
94 | # https://nextjs.org/blog/next-9-1#public-directory-support
95 | # public
96 |
97 | # vuepress build output
98 | .vuepress/dist
99 |
100 | # Serverless directories
101 | .serverless/
102 |
103 | # FuseBox cache
104 | .fusebox/
105 |
106 | # DynamoDB Local files
107 | .dynamodb/
108 |
109 | # TernJS port file
110 | .tern-port
111 |
--------------------------------------------------------------------------------
/script/script.js:
--------------------------------------------------------------------------------
1 | // todo Book Class: Represents a Book
2 | class Book {
3 | constructor(title, author, isbn) {
4 | this.title = title;
5 | this.author = author;
6 | this.isbn = isbn;
7 | }
8 | }
9 |
10 | // todo UI Class: Handle UI Tasks
11 | class UI {
12 | static displayBooks() {
13 |
14 | const books = Store.getBooks();
15 |
16 | books.forEach((book) => UI.addBookToList(book));
17 | }
18 |
19 | static addBookToList(book) {
20 | const list = document.querySelector('#book-list');
21 |
22 | const row = document.createElement('tr');
23 | row.innerHTML = `
24 | ${book.title}
25 | ${book.author}
26 | ${book.isbn}
27 | X
28 | `;
29 |
30 | list.appendChild(row);
31 | }
32 |
33 | static deleteBook(el) {
34 | if(el.classList.contains('delete')) {
35 | el.parentElement.parentElement.remove();
36 | }
37 | }
38 |
39 | static showAlert(message, className) {
40 | const div = document.createElement('div');
41 |
42 | div.className = `alert alert-${className}`;
43 | div.appendChild(document.createTextNode(message));
44 |
45 | const container = document.querySelector('.container');
46 | const form = document.querySelector('#book-form');
47 | container.insertBefore(div, form);
48 |
49 | setTimeout(() => document.querySelector('.alert').remove(), 3000);
50 | }
51 |
52 | static clearFields() {
53 | document.querySelector('#title').value = '';
54 | document.querySelector('#author').value = '';
55 | document.querySelector('#isbn').value = '';
56 | }
57 | }
58 |
59 | // todo Store Class: Handles Storage
60 | class Store {
61 | static getBooks() {
62 |
63 | let books;
64 |
65 | if (localStorage.getItem('books') === null) {
66 | books = [];
67 | } else {
68 | books = JSON.parse(localStorage.getItem('books'));
69 | }
70 | return books;
71 |
72 | }
73 |
74 | static addBook(book) {
75 | const books = Store.getBooks();
76 |
77 | books.push(book);
78 |
79 | localStorage.setItem('books', JSON.stringify(books));
80 |
81 | }
82 |
83 | static removeBook(isbn) {
84 | const books = Store.getBooks();
85 |
86 | books.forEach((book, index) => {
87 | if(book.isbn === isbn) {
88 | books.splice(index, 1);
89 | }
90 | });
91 |
92 | localStorage.setItem('books', JSON.stringify(books));
93 | }
94 | }
95 |
96 |
97 | // todo Events: Display Books
98 | document.addEventListener('DOMContentLoaded', UI.displayBooks);
99 |
100 | // todo Events: Add a Book
101 |
102 | document.querySelector('#book-form').addEventListener('submit', (e) => {
103 |
104 |
105 | e.preventDefault();
106 |
107 | const title = document.querySelector('#title').value;
108 | const author = document.querySelector('#author').value;
109 | const isbn = document.querySelector('#isbn').value;
110 |
111 | if(title === '' || author === '' || isbn === '') {
112 |
113 | UI.showAlert('Please fill in all fields', 'danger');
114 |
115 | } else {
116 |
117 | const book = new Book(title, author, isbn);
118 |
119 | UI.addBookToList(book);
120 |
121 | Store.addBook(book);
122 |
123 | UI.showAlert('Book Added', 'success');
124 |
125 | UI.clearFields();
126 |
127 | }
128 | })
129 |
130 | // todo Events: Remove a Book
131 | document.querySelector('#book-list').addEventListener('click', (e) => {
132 |
133 | UI.deleteBook(e.target);
134 |
135 | Store.removeBook(e.target.parentElement.previousElementSibling.textContent);
136 |
137 | UI.showAlert('Book Removed', 'success');
138 | });
139 |
--------------------------------------------------------------------------------