امیدوارم که توانسته باشید تا این قسمت پیش بیایید. حالا که قسمت اعظم برنامه را کدنویسی کرده ایم فقط بحث حذف کتاب ها و تبدیل به کلاس های ES6 مانده است. در پروژه ای که پیش روی ما است حروف x کوچکی وجود دارد که برای حذف کتاب ها در نظر گرفته شده اند اما مشکلی وجود دارد. در زمان بارگذاری سایت و شروع برنامه هیچ کتابی وجود ندارد بنابراین هیچ دکمه ی حذفی نیز وجود ندارد و نمی توانیم در همان ابتدا این دکمه های حذف را درون یک const قرار دهیم. حتی اگر یک دکمه ی حذف وجود داشته باشد، بقیه ی دکمه هایی که بعدا توسط کاربر اضافه می شوند را چه کار کنیم؟
اگر یادتان باشد در چندین قسمت قبل و به زبان ساده مفهومی به نام event delegation را توضیح داده بودم که می توانید به آن مراجعه کنید. در اینجا هم راه حل همان event delegation است! بنابراین به انتهای فایل App.js و پس از event-listener فعلی بروید و یک event-listener دیگر ایجاد کنید:
// Event Listener for delete
document.getElementById('book-list').addEventListener('click', function (e) {
e.preventDefault();
}
همانطور که می بینید بر اساس مفهوم event delegation به جای هدف گرفتن دکمه های حذف (حروف x) پدر آن ها را (خود لیست) هدف قرار گرفته ایم. سپس اولین کاری که کرده ایم preventDefault کردن فرم بوده است تا چیزی ثبت نشود. من نمی خواهم کدهای بیشتری را به این قسمت اضافه کنم بلکه آن را به صورت یک متد در prototype می نویسم. من بالا تر از متد clearFields می نویسم:
// Delete Book
UI.prototype.deleteBook = function(target){
if(target.className === 'delete') {
target.parentElement.parentElement.remove();
}
}
سوال: پارامتر target به صورت خودکار به این متد پاس داده خواهد شد. آیا می دانید چرا؟
پاسخ: همانطور که می دانید، شیء target به صورت خودکار درون event-listener ها وجود دارد و می توانید هر جایی از آن استفاده کنید. target به هدف آن event-listener اشاره می کند؛ به طور مثال اگر روی نام کتاب کلیک کنیم، target می شود قسمت «نام کتاب». با اینکه این متد خارج از event-listener ما تعریف شده است اما بعدا آن را برای اجرا، درون event-listener فراخوانی خواهیم کرد بنابراین شیء target را نیز به صورت دستی به آن پاس خواهیم داد.
قبل از اینکه این کد را توضیح بدهم باید به ساختار کلید های حذف کتاب (حروف X) در فایل HTML نگاهی بیندازیم. اگر یادتان باشد کد زیر مسئول ساخت این کلید ها در فایل HTML بود:
// Add Book To List
UI.prototype.addBookToList = function(book){
const list = document.getElementById('book-list');
// Create tr element
const row = document.createElement('tr');
// Insert cols
row.innerHTML = `
<td>${book.title}</td>
<td>${book.author}</td>
<td>${book.isbn}</td>
<td><a href="#" class="delete">X<a></td>
`;
list.appendChild(row);
}

بنابراین یک تگ <a> درون <td> ما وجود دارد که همان حرف x است و کلاس delete را نیز دارد. همچنین برای حذف کامل یک ردیف از جدول باید تمام <tr> را حذف کنیم. از آنجایی که کلید حذف (کلاس delete) درون <td> است باید این مسیر را تا <tr> مسیر دهی کرده و سپس آن را حذف کنیم. حالا متوجه کد زیر می شوید؟
// Delete Book
UI.prototype.deleteBook = function(target){
if(target.className === 'delete') {
target.parentElement.parentElement.remove();
}
}
بله <tr> پدر دوم تگ <a> است بنابراین دوبار از parentElement استفاده کرده ایم تا به آن برسیم. حالا می توانیم به event-listener خود برگردیم و از این متد در آن استفاده کنیم:
// Event Listener for delete
document.getElementById('book-list').addEventListener('click', function(e){
// Instantiate UI
const ui = new UI();
// Delete book
ui.deleteBook(e.target);
// Show message
ui.showAlert('Book Removed!', 'success');
e.preventDefault();
});
از آنجایی که متد deleteBook در prototype مربوط به UI قرار دارد برای استفاده از آن باید یک نمونه (instance) از UI را بسازیم (new UI). سپس از متد deleteBook استفاده کرده ایم و همانطور که گفتم target را به صورت دستی به آن پاس داده ایم. در نهایت نیز با استفاده از متد showAlert پیام حذف موفقیت آمیز ردیف را نمایش خواهیم داد. اگر کد بالا را در مرورگر خود تست کنید، باید بدون هیچ نقصی کار کند. در صورت برخورد با مشکل سعی کنید دوباره کدهایتان را بررسی کنید و یا کدهای خود را با سورس کد این جلسه مقایسه نمایید (دانلود در انتهای این مقاله).
حالا که پروژه را به صورت کامل کدنویسی کردیم باید یک بار هم آن را با کلاس های ES6 بنویسیم تا با نحوه ی استفاده از آن ها نیز آشنا شویم. اولین کاری که باید انجام دهید، مراجعه به فایل index.html و تبدیل دستور script به appes6.js است:
<script src="appes6.js"></script>
حالا وارد فایل appes6.js شوید تا کار را شروع کنیم. اگر فایل app.js را به یاد داشته باشید، در آن دو constructor برای Book و UI داشتیم، بنابراین باید در appes6.js نیز دو کلاس به نام های Book و UI ایجاد کنیم:
class Book {
}
class UI {
}
کلاس Book بسیار ساده خواهد بود. در واقع فقط یک constructor خواهد داشت:
class Book {
constructor(title, author, isbn) {
this.title = title;
this.author = author;
this.isbn = isbn;
}
}
این کد دقیقا معادل کد زیر در app.js است:
// Book Constructor
function Book(title, author, isbn) {
this.title = title;
this.author = author;
this.isbn = isbn;
}
حالا به سراغ کلاس UI می رویم. متد اول ما addBookToList بود، همچنین متدهای دیگری را نیز داشتیم. بنابراین ابتدا بدنه ی خالی تمام متدهایمان را وارد فایل appes6.js می کنیم:
class UI {
addBookToList(book) {
}
showAlert(message, className) {
}
deleteBook(target) {
}
clearFields() {
}
}
در همین ابتدا متوجه می شوید که استفاده از کلاس ها چقدر تمیزتر و جمع و جور تر است! کدهای درون این متدها دقیقا مانند ES5 عمل می کند. بنابراین می توانیم بدنه ی این توابع را از فایل App.js کپی کنیم:
class UI {
addBookToList(book) {
const list = document.getElementById('book-list');
// Create tr element
const row = document.createElement('tr');
// Insert cols
row.innerHTML = `
<td>${book.title}</td>
<td>${book.author}</td>
<td>${book.isbn}</td>
<td><a href="#" class="delete">X<a></td>
`;
list.appendChild(row);
}
showAlert(message, className) {
// Create div
const div = document.createElement('div');
// Add classes
div.className = `alert ${className}`;
// Add text
div.appendChild(document.createTextNode(message));
// Get parent
const container = document.querySelector('.container');
// Get form
const form = document.querySelector('#book-form');
// Insert alert
container.insertBefore(div, form);
// Timeout after 3 sec
setTimeout(function(){
document.querySelector('.alert').remove();
}, 3000);
}
deleteBook(target) {
if(target.className === 'delete') {
target.parentElement.parentElement.remove();
}
}
clearFields() {
document.getElementById('title').value = '';
document.getElementById('author').value = '';
document.getElementById('isbn').value = '';
}
}
این کلاس UI نیز کامل شده است و برای بقیه ی این موارد نیز می توانیم کدهای app.js را کپی کنیم:
class Book {
constructor(title, author, isbn) {
this.title = title;
this.author = author;
this.isbn = isbn;
}
}
class UI {
addBookToList(book) {
const list = document.getElementById('book-list');
// Create tr element
const row = document.createElement('tr');
// Insert cols
row.innerHTML = `
<td>${book.title}</td>
<td>${book.author}</td>
<td>${book.isbn}</td>
<td><a href="#" class="delete">X<a></td>
`;
list.appendChild(row);
}
showAlert(message, className) {
// Create div
const div = document.createElement('div');
// Add classes
div.className = `alert ${className}`;
// Add text
div.appendChild(document.createTextNode(message));
// Get parent
const container = document.querySelector('.container');
// Get form
const form = document.querySelector('#book-form');
// Insert alert
container.insertBefore(div, form);
// Timeout after 3 sec
setTimeout(function(){
document.querySelector('.alert').remove();
}, 3000);
}
deleteBook(target) {
if(target.className === 'delete') {
target.parentElement.parentElement.remove();
}
}
clearFields() {
document.getElementById('title').value = '';
document.getElementById('author').value = '';
document.getElementById('isbn').value = '';
}
}
// Event Listener for add book
document.getElementById('book-form').addEventListener('submit', function(e){
// Get form values
const title = document.getElementById('title').value,
author = document.getElementById('author').value,
isbn = document.getElementById('isbn').value
// Instantiate book
const book = new Book(title, author, isbn);
// Instantiate UI
const ui = new UI();
console.log(ui);
// Validate
if(title === '' || author === '' || isbn === '') {
// Error alert
ui.showAlert('Please fill in all fields', 'error');
} else {
// Add book to list
ui.addBookToList(book);
// Show success
ui.showAlert('Book Added!', 'success');
// Clear fields
ui.clearFields();
}
e.preventDefault();
});
// Event Listener for delete
document.getElementById('book-list').addEventListener('click', function(e){
// Instantiate UI
const ui = new UI();
// Delete book
ui.deleteBook(e.target);
// Show message
ui.showAlert('Book Removed!', 'success');
e.preventDefault();
});
به همین راحتی توانستیم پروژه ی خودمان را از ES5 به ES6 بیاوریم. درست است که این پروژه یک پروژه ی ساده بود اما انجام این کار برای پروژه های سنگین نیز آنچنان سخت نیست. باور ندارید؟ خودتان امتحان کنید! در قسمت بعد تمام این پروژه را به Local Storage نیز اضافه می کنم تا با refresh شدن صفحه، محتوای آن از بین نرود.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.