در قسمت قبل کاری کردیم که با کلیک روی Add Task یک task جدید در لیست ما نمایش داده شود و در این قسمت می خواهیم کدها را تکمیل کنیم تا با کلیک روی علامت های ضربدر، task مورد نظر حذف شود:
همانطور که در مثال بالا می بینید، ما دو task داریم (بیشتر از 1 عدد) و از طرفی قرار است آن ها را به صورت پویا حذف یا اضافه کنیم به همین خاطر باید از Event Delegation استفاده کنیم. بنابراین بهتر است ابتدا Event Delegation را برایتان توضیح دهم. فرض کنید کد HTML ما بدین شکل باشد:
<ul id="parent-list"> <li id="post-1">Item 1</li> <li id="post-2">Item 2</li> <li id="post-3">Item 3</li> <li id="post-4">Item 4</li> <li id="post-5">Item 5</li> <li id="post-6">Item 6</li> </ul>
حالا فرض کنید که قرار است با کلیک روی هر کدام از این li ها اتفاق خاصی بیفتد (مهم نیست چه اتفاقی). یکی از روش های انجام این کار اضافه کردن event-listener به تک تک li ها می باشد اما اگر بعدا یک li به این لیست اضافه کنیم، چطور؟ آن گاه باید برگردیم و سورس کد را تغییر دهیم تا برای li جدید نیز یک event-listener داشته باشیم! اگر بخواهیم این کار را در جای جای برنامه مان انجام بدهیم، کار عملا غیرممکنی می شود چرا که باید هر روز سورس کد برنامه را تغییر دهیم! روش بهتر این است که event-listener را به عنصر پدر (یعنی UL ) بدهیم.
سوال: جاوا اسکریپت چطور می فهمد روی کدام عنصر کلیک شده است؟
پاسخ: این کار بر عهده ی شما است. شما با استفاده از target عنصر می توانید آن را تشخیص دهید. به طور مثال اگر برای کد HTML بالا بخواهیم چیزی بنویسیم، می گوییم:
// Get the element, add a click listener... document.getElementById("parent-list").addEventListener("click", function(e) { // e.target is the clicked element! // If it was a list item if(e.target && e.target.nodeName == "LI") { // List item found! Output the ID! console.log("List item ", e.target.id.replace("post-", ""), " was clicked!"); } });
یعنی ابتدا یک event-listener به عنصر UL داده ایم، سپس چک کرده ایم که روی عنصر کلیک شده باشد و عنصر کلیک شده یک LI باشد (چرا که ممکن است روی خود UL یا عنصر دیگری کلیک شود). در صورتی که شرط ها بر قرار بود کار خاصی را انجام می دهیم (مثلا در این قسمت متنی را console.log کرده ایم.
به این مفهوم Event Delegation می گویند.
در پروژه ی خودمان نیز باید از event delegation استفاده کنیم چرا که قرار است Task های جدیدی به برنامه اضافه شوند. بنابراین به درون تابع loadEventListeners میرویم و این event جدید را به UL اضافه می کنیم:
function loadEventListeners() { // Add task event form.addEventListener('submit', addTask); // Remove task event taskList.addEventListener('click', removeTask); }
این event قرار است تابع removeTask را اجرا کند که باید آن را کدنویسی کنیم. به پایین تر از تابع addTask میرویم و این تابع جدید را تعریف می کنیم:
function removeTask(e) { console.log(e.target); }
اگر الان روی علامت ضربدر کلیک کنیم در قسمت console مرورگر چه چیزی به ما برگردانده می شود؟ بله تگ i که برای آیکون ما بود برگردانده می شود:
در حالی که ما تگ a را می خواهیم که پدر تگ i است. حتما کدهای HTML پروژه را به یاد دارید:
بنابراین باید شرطی را تعیین کنیم که اگر عنصر کلیک شده پدری با کلاس delete-item داشت آن گاه اتفاق خاصی بیفتد:
// Remove Task function removeTask(e) { if (e.target.parentElement.classList.contains('delete-item')) { console.log(e.target); } }
البته اینجا فعلا console.log کرده ایم اما باید کاری کنیم که با کلیک روی علامت آیکون (تگ <i>) تمام تگ <li> حذف شود که یعنی پدرِ پدرِ تگ <i> حذف شود (پدر اول تگ <i>، تگ <a> و پدر دوم آن خود <li> است). بنابراین به جای console.log کردن، باید بگوییم:
function removeTask(e) { if (e.target.parentElement.classList.contains('delete-item')) { e.target.parentElement.parentElement.remove(); } }
حالا اگر کد را در مرورگر امتحان کنید، بدون هیچ مشکلی اجرا خواهد شد اما بهتر است کد را درون یک confirmation بگذاریم تا کاربر به اشتباه چیزی را حذف نکند:
// Remove Task function removeTask(e) { if (e.target.parentElement.classList.contains('delete-item')) { if (confirm('Are You Sure?')) { e.target.parentElement.parentElement.remove(); } } }
حالا نوبت به کدنویسی دکمه ی Clear Tasks میرسد. کار این دکمه این است که تمام Task ها را یکجا حذف کند. برای این دکمه نیز یک event-listener در تابع loadEventListeners اضافه می کنیم:
// Load all event listeners function loadEventListeners() { // Add task event form.addEventListener('submit', addTask); // Remove task event taskList.addEventListener('click', removeTask); // Clear task event clearBtn.addEventListener('click', clearTasks); }
قرار است این event یک تابع به نام clearTasks را ایجاد کند که باید آن را نیز کدنویسی کنیم. شما می توانید این تابع را با روش های مختلفی اجرا کنید؛ در روش اول از innerHTML استفاده می کنیم:
// Clear Tasks function clearTasks() { taskList.innerHTML = ''; }
اگر دقت کنید <li> ها innerHTML عنصر TaskList حساب می شوند بنابراین با خالی کردن innerHTML آن میتوانیم همه ی آن را حذف کنیم. در روش دوم از یک حلقه ی while استفاده می کنیم و می گوییم تا زمانی که taskList دارای فرزند است (تگ های <li> آن فرزند را حذف کن. من روش اول را کامنت کرده و روش دوم را اضافه می کنم:
// Clear Tasks function clearTasks() { // taskList.innerHTML = ''; // Faster while(taskList.firstChild) { taskList.removeChild(taskList.firstChild); } }
بنابر برخی تست های انجام شده ظاهرا روش دوم سریع تر از روش اول است اما در عمل آنچنان تفاوتی ندارند. با این حال من برایتان کامنت کرده ام که روش دوم Faster (سریع تر) است.
برای اطلاعات بیشتر به وبسایت https://jsperf.com/innerhtml-vs-removechild/47 مراجعه کنید. در قسمت بعدی می خواهیم کاری کنیم که بتوانیم task ها را فیلتر کرده یا به عبارتی دیگر بین آن ها جست و جو انجام دهیم.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.