My Approach
The code for the library project is all over the place, how would you code this project using object-oriented programming. For example, would you use
document.addEventListener()
and just set if statements for elements that are dynamically created?
Also, there is not a lot of comments in this code, I thought my naming for each variable made sense and so comments were unneeded. Do you disagree?
I used a closure in my render function but that's actually my first time using a closure so i don't know if that is correct or would it just involve unexpected bugs.
I did this project for The Odin Project Here's the link the project and steps to take https://www.theodinproject.com/courses/javascript/lessons/library
/*------------------------------------------
Variables
-------------------------------------------*/
let myLibrary = [];
let bookIndex = 0;
let mainRender = render();
/*------------------------------------------
Constructor
-------------------------------------------*/
function Book(title, author, pages, status) {
this.title = title;
this.author = author;
this.pages = pages;
this.status = status;
}
/*------------------------------------------
Functions
-------------------------------------------*/
function addBookToLibrary() {
const title = document.querySelector('#title').value;
const author = document.querySelector('#author').value;
const pages = document.querySelector('#pages').value;
const status = document.querySelector('#status').value;
const book = new Book(title, author, pages, status);
myLibrary.push(book);
}
function render() {
let i = 0;
return function() {
const bookshelf = document.querySelector('.bookshelf__creation');
for (; i < myLibrary.length; i++) {
bookshelf.insertAdjacentHTML('beforeend', bookTemplate(myLibrary[i]));
}
}
}
function bookTemplate(book) {
const statusBtnColor = book.status == 'unread' ? 'status__btn status__btn--change' : 'status__btn';
const HTML = `
<tr class="bookshelf__book" data-index="${bookIndex++}">
<td class="title">${book.title}</td>
<td class="author">${book.author}</td>
<td class="pages">${book.pages}</td>
<td class="status"><button id="status-btn" class="${statusBtnColor}">${book.status}</button></td>
<td class="delete"><button class="btn btn--primary">x</button></td>
</tr>`;
return HTML;
}
function toggleStatus(event) {
const index = event.target.parentElement.parentElement.dataset.index;
if (event.target.textContent == 'read') {
event.target.textContent = 'unread';
myLibrary[index]['status'] = 'unread';
event.target.classList.add('status__btn--change');
} else if (event.target.textContent == 'unread') {
event.target.textContent = 'read';
myLibrary[index]['status'] = 'read';
event.target.classList.remove('status__btn--change');
}
}
function removeBook(event) {
const parentOfBookInfo = event.target.parentNode.parentNode;
parentOfBookInfo.parentNode.remove();
myLibrary = myLibrary.filter( book => {
console.log(`${book.title}\n${parentOfBookInfo.querySelector(".title").textContent}`);
return book.title !== parentOfBookInfo.querySelector(".title").textContent;
});
}
function emptyInputs() {
const modalInputs = document.querySelectorAll('.modal__input');
modalInputs.forEach(input => input.value = '');
}
/*------------------------------------------
Event Listeners
-------------------------------------------*/
document.addEventListener('click', event => {
const modal = document.querySelector('.modal');
if (event.target.className == 'btn btn--primary') removeBook(event);
if (event.target.id == 'status-btn') toggleStatus(event);
if (event.target.className == 'bookshelf__open-modal') modal.style.display = 'block';
if (event.target.className == 'modal__add') {
modal.style.display = 'none';
addBookToLibrary();
emptyInputs();
mainRender();
}
if (event.target.className == 'modal__cancel') {
modal.style.display = 'none';
emptyInputs();
}
});
* {
margin: 0;
padding: 0;
-webkit-box-sizing: border-box;
box-sizing: border-box;
font-family: 'Roboto', sans-serif;
}
.background {
padding: 2% 4%;
margin-bottom: 2em;
height: 12rem;
width: 100%;
color: #fff;
background: linear-gradient(304deg, #020024 0%, #3a3d3d 0%, #282828 94%);
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
}
.background__heading {
margin-bottom: 0.2em;
font-size: 2.5rem;
}
.bookshelf {
max-width: 90%;
margin: 0 auto;
}
.bookshelf__heading {
font-size: 2rem;
font-weight: 700;
}
.bookshelf__creation {
text-align: left;
}
.bookshelf table {
border-collapse: collapse;
margin: 2em 0;
}
.bookshelf table th {
padding: 0.6em 1em;
}
.bookshelf__book {
border-top: 2px solid #ececec;
}
.bookshelf__book td {
padding: 1em;
text-align: center;
}
.bookshelf__open-modal {
padding: 0.7rem;
border: none;
background-color: #383838;
color: white;
cursor: pointer;
}
.status__btn {
padding: 0.4rem 2rem;
border: none;
background-color: #ececec;
cursor: pointer;
}
.status__btn--change {
background-color: #383838;
color: white;
}
.delete .btn {
width: 1.5rem;
height: 1.5rem;
color: white;
border-radius: 50%;
text-align: center;
border: none;
font-weight: 700;
}
.modal {
padding: 1.25rem;
width: 100%;
position: absolute;
top: 0;
background: white;
max-width: 50%;
margin: 0 auto;
-webkit-transform: translate(50%, 20%);
transform: translate(50%, 20%);
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
border-radius: 6px;
-webkit-box-shadow: 0 0.5em 1em -0.125em rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.02);
box-shadow: 0 0.5em 1em -0.125em rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.02);
color: #4a4a4a;
}
.modal .modal__add {
padding: 0.7rem;
border: none;
background-color: #383838;
color: white;
cursor: pointer;
}
.modal .modal__cancel {
padding: 0.7rem;
border: 1.5px solid lightgray;
border-radius: 5px;
background-color: #fff;
cursor: pointer;
}
.modal h2 {
margin-bottom: 1em;
}
.modal * {
-ms-flex-item-align: self-start;
-ms-grid-row-align: self-start;
align-self: self-start;
}
.modal label {
font-size: 1rem;
font-weight: 600;
margin-bottom: 0.4em;
}
.modal input {
width: 100%;
margin-bottom: 1em;
height: 3em;
border: 1.5px solid lightgray;
border-radius: 5px;
}
.modal select {
width: 6rem;
height: 2rem;
border: 1.5px solid lightgray;
border-radius: 5px;
background-color: #fff;
margin-bottom: 1em;
cursor: pointer;
}
tbody:nth-child(even) {
background-color: whitesmoke;
border-top: 1px solid #a0a0a0;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Librario</title>
<!-- links -->
<link href="https://fonts.googleapis.com/css?family=Roboto:400,700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="./index.css">
</head>
<body>
<div class="container">
<header class="background">
<h1 class="background__heading">Librario</h1>
<p class="background__intro-text">Your Pocket Bookshelf</p>
</header>
<div class="bookshelf">
<h2 class="bookshelf__heading">Books</h2>
<table class="bookshelf__creation">
<tr class="table">
<th class="table__heading">Title</th>
<th class="table__heading">Author</th>
<th class="table__heading">Pages</th>
<th class="table__heading">Status</th>
</tr>
<!-- JavaScript make this -->
<!-- <tr class="bookshelf__book" data-newBook="">
<td class="title">Harry Potter and the Sorceror Stone</td>
<td class="author">J.K Rowling</td>
<td class="pages">890</td>
<td class="status"><button class="status__btn">Read</button></td>
<td class="delete"><button class="btn btn--primary">x</button></td>
</tr>
<tr class="bookshelf__book" data-newBook="">
<td class="title">Harry Potter and the Sorceror Stone</td>
<td class="author">J.K Rowling</td>
<td class="pages">890</td>
<td class="status"><button class="status__btn">Read</button></td>
<td class="delete"><button class="btn btn--primary">x</button></td>
</tr> -->
</table>
<button class="bookshelf__open-modal">Add New Book</button>
</div>
<div class="modal" style="display: none;">
<h2>Add New Book</h2>
<label for="title">Title</label>
<input class="modal__input" id="title" type="text">
<label for="author">Author</label>
<input class="modal__input" id="author" type="text">
<label for="pages">Number of Pages</label>
<input class="modal__input" id="pages" type="text">
<label for="status">Read Status</label>
<select id="status">
<option value="read">Read</option>
<option value="unread">Unread</option>
</select>
<div class="modal__btn-container">
<button class="modal__add">Add Book</button>
<button class="modal__cancel">Cancel</button>
</div>
</div>
</div>
<!-- Scripts -->
<script src="./index.js"></script>
</body>
</html>