پروژه ی ایجاد To-Do List: خواندن task ها از local storage

13 آبان 1398
پروژه ی ایجاد To-Do List: خواندن task ها از local storage

خواندن task ها از local storage

تا این قسمت توانسته ایم task هایمان را درون local storage ذخیره کنیم تا با reload شدن صفحه از بین نروند. حالا زمان آن است که این مقادیر را از local storage دریافت کرده و در برنامه وارد کنیم. برای انجام این کار ابتدا به یک event-listener احتیاج داریم بنابراین در تابع loadEventListeners خودمان این مورد را نیز اضافه می کنیم:

function loadEventListeners() {
    // DOM Load Event
    document.addEventListener('DOMContentLoaded', getTasks);

    // Add task event
    form.addEventListener('submit', addTask);

    // Remove task event
    taskList.addEventListener('click', removeTask);

    // Clear task event
    clearBtn.addEventListener('click', clearTasks);

    // Filter tasks event
    filter.addEventListener('keyup', filterTasks);
}

همانطور که می بینید منتظر رویداد DOMContentLoaded (به معنی «محتوای DOM بارگذاری شد») هستیم و آن را مستقیما به document متصل کرده ایم. این event-listener تابعی به نام getTasks را اجرا خواهد کرد که باید آن را بنویسیم، بنابراین:

// Get Tasks from LS
function getTasks() {
    let tasks;
    if (localStorage.getItem('tasks') === null) {
        tasks = [];
    } else {
        tasks = JSON.parse(localStorage.getItem('tasks'));
    }
}

در ابتدا همان کاری را می کنیم که در جلسه ی قبل انجام دادیم؛ اگر درون local storage چیزی بود آن را به متغیر tasks منتسب می کنیم و در غیر این صورت مقدارش را برابر با یک آرایه ی خالی قرار می دهیم. حالا باید بین این task های مختلف گردش کنیم و مقدارشان را دریافت و روی صفحه نمایش دهیم:

// Get Tasks from LS
function getTasks() {
    let tasks;
    if (localStorage.getItem('tasks') === null) {
        tasks = [];
    } else {
        tasks = JSON.parse(localStorage.getItem('tasks'));
    }

    tasks.forEach(function (task) {
        // Create li element
        const li = document.createElement('li');
        // Add class
        li.className = 'collection-item';
        // Create text node and append to li
        li.appendChild(document.createTextNode(task));
        // Create new link element
        const link = document.createElement('a');
        // Add class
        link.className = 'delete-item secondary-content';
        // Add icon html
        link.innerHTML = '<i class="fa fa-remove"></i>';
        // Append the link to li
        li.appendChild(link);

        // Append li to ul
        taskList.appendChild(li);
    });
}

توضیحات کد:

در این کد می خواهیم بین مقادیر مختلف در local storage گردش کنیم و برای هر مقدار یک <li> بسازیم تا درون Task list ما نمایش داده شود بنابراین کدهای جلسات قبل برای ساخت <li> را از تابع addTask کپی کرده ایم و درون تابع جدیدمان قرار داده ایم. تنها مقداری که تغییر داده ایم در قسمت Create text node and append to li است که آن را از taskInput.value به task تغییر داده ایم چرا که هنوز کاربر چیزی تایپ نکرده و اصلا taskInput ای نداریم. به عنوان یادآوری، به صورت خلاصه و به ترتیب خطوط کدهای درون forEach این کار ها را انجام می دهند:

  • دستور createElement عنصر <li> را می سازد.
  • className کلاس مورد نظر را به <li> اضافه می کند.
  • appendChild یک text node ایجاد می کند و آن را به <li> می چسباند. این text node همان مقداری است که در local storage ذخیره شده است.
  • سپس createElement یک عنصر <a> می سازد.
  • className کلاس مورد نظر را به آن می دهد (کلاس های delete-item secondary-content).
  • با استفاده از innerHTML یک تگ <i> به آن اضافه می کنیم تا آیکون ضربدر نمایش داده شود.
  • در آخر با appendChild آن را به <li> میچسبانیم و آن <li> را به task list اضافه می کنیم.

حالا مشکلی در حذف این موارد داریم. به طور مثال اگر من روی علامت ضربدر کلیک کنم و یکی از task ها را حذف کنم، پس از refresh کردن صفحه دوباره برمی گردد و در اصل از local storage حذف نمی شود. برای حل این مشکل باید هنگام حذف یک task، آن را از local storage نیز حذف کنیم. اگر یادتان باشد تابعی به نام removeTasks وجود داشت که کار حذف این موارد را انجام می داد:

// Remove Task
function removeTask(e) {
    if (e.target.parentElement.classList.contains('delete-item')) {
        if (confirm('Are You Sure?')) {
            e.target.parentElement.parentElement.remove();
        }
    }
}

ما باید درون همین تابع کدهای حذف از local storage را نیز بنویسیم، بنابراین:

// Remove Task
function removeTask(e) {
    if (e.target.parentElement.classList.contains('delete-item')) {
        if (confirm('Are You Sure?')) {
            e.target.parentElement.parentElement.remove();

            // remove from LS
            removeTaskFromLocalStorage(e.target.parentElement.parentElement);
        }
    }
}

برای تمیز بودن کدها، فقط نام تابع را درون removeTasks نوشته ام تا همزمان با اجرایش removeTaskFromLocalStorage را نیز اجرا کند. همچنین زمانی که می گوییم مورد x را حذف کن باید مشخص کنیم این x دقیقا چیست اما از آنجایی که id خاصی برای <li> هایمان نداریم باید از مقدار e.target.parentElement.parentElement استفاده کنیم که آن را به تابع هم پاس داده ایم. حالا بیرون از تابع removeTasks، تابع جدید را کدنویسی می کنیم:

// Remove from LS
function removeTaskFromLocalStorage(taskItem) {
    let tasks;
    if (localStorage.getItem('tasks') === null) {
        tasks = [];
    } else {
        tasks = JSON.parse(localStorage.getItem('tasks'));
    }

    tasks.forEach(function (task, index) {
        if (taskItem.textContent === task) {
            tasks.splice(index, 1);
        }
    });

    localStorage.setItem('tasks', JSON.stringify(tasks));
}

توضیحات کد:

اولین کاری که انجام می دهیم ایجاد متغیر tasks است تا بدانیم چیزی در local storage وجود دارد یا خیر. از آنجایی که این کد عینا تکرار موارد قبلی است می توانید آن را از توابع دیگر کپی کنید و مشکلی نخواهیم داشت. در مرحله ی بعد با استفاده از یک حلقه ی forEach بین موارد موجود در local storage (که در اینجا می شود همان متغیر tasks) گردش کرده ایم. درون آن یک شرط if داریم که می گوید اگر taskItem.textContent (یعنی خود متن task - مثلا roxo.ir در مثال خودمان) برابر با task بود، میدانیم که باید حذفش کنیم. task در هر گردش برابر یکی از مقادیر موجود در local storage است؛ به زبان ساده تر اگر مقدار حذف شده (متن <li>) برابر با مقدار موجود در local storage باشد، باید آن مقدار نیز از local storage حذف شود. برای حذف کردن این مقدار از تابع splice استفاده کرده ایم. یادتان باشد که حلقه ی forEach به صورت پارامتر دوم index فعلی در هر گردش را نیز به شما می دهد بنابراین می توانیم از آن استفاده کنیم تا به Splice بگوییم index موردنظر را پیدا کن و سپس 1 مورد را حذف کن. توجه داشته باشید که تا این قسمت برای خودمان task را حذف کرده ایم و هنوز چیزی به local storage نگفته ایم بنابراین بدون هیچ تغییری باقی مانده است. در نهایت باید Local storage را دوباره تنظیم کنیم تا به روز رسانی شود.

حالا اگر roxo.ir را حذف کنیم و صفحه را refresh کنیم هیچ مشکلی نخواهیم داشت. شما می توانید کد را خودتان تست نمایید:

حذف نشدن roxo.ir حتی پس از refresh صفحه
حذف نشدن roxo.ir حتی پس از refresh صفحه

آخرین کاری که باید انجام دهیم اضافه کردن کارایی به دکمه ی Clear Tasks می باشد. اگر task ها را با استفاده از این دکمه حذف کنیم، از local storage حذف نمی شوند، بنابراین باید درون تابع clearTasks کدهای این مورد را نیز اضافه کنیم:

// Clear Tasks
function clearTasks() {
    // taskList.innerHTML = '';

    // Faster
    while (taskList.firstChild) {
        taskList.removeChild(taskList.firstChild);
    }

    // Clear from LS
    clearTasksFromLocalStorage();
}

تابعی به نام clearTasksFromLocalStorage نوشته ایم بنابراین باید خارج از تابع clearTasks آن را تعریف کنیم:

// Clear Tasks from LS
function clearTasksFromLocalStorage() {
  localStorage.clear();
}

صدا زدن دستور clear روی localStorage تمام local storage را حذف می کند.

با شما تبریک میگویم! شما با موفقیت برنامه ی مدرن to-do list را تمام کردید!

دانلود کدهای کامل شده ی این پروژه.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری پروژه‌های مدرن جاوا اسکریپت توصیه می‌کند:
نویسنده شوید

دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.