Приложение todo на javascript

Подскажите, как сделать так, чтобы при нажатии кнопок готово и удалить вносились изменения в todoItemsDefault и далее записывались измененные значения в localStorage.

<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
    <script defer src="todo-app.js"></script>
    <script>
        let defaultTasks = [{name: 'Сходить в магазин', done: true}, {name: 'Купить хлеб', done: false}]
        document.addEventListener('DOMContentLoaded', function() {
            createTodoApp(document.getElementById('todo-app'), 'Мои дела', defaultTasks);
        })
    </script>
    <title>TODO</title>
</head>
<body>

    <div class="container mb-5">
        <nav class="nav">
            <a class="nav-link" href="index.html">Мои дела</a>
            <a class="nav-link" href="mom.html">Дела мамы</a>
            <a class="nav-link" href="dad.html">Дела папы</a>
        </nav>
    </div>

    <div id="todo-app" class="container"></div>

</body>
</html>

(function() {
    // Создаём и возвращаем заголовок приложения
    function createAppTitle(title) {
        let appTitle = document.createElement('h2');
        appTitle.innerHTML = title;
        return appTitle;
    }

    // Создаём и возвращаем форму для создания дела
    function createTodoItemForm() {
        let form = document.createElement('form');
        let input = document.createElement('input');
        let buttonWrapper = document.createElement('div');
        let button = document.createElement('button');

        form.classList.add('input-group', 'mb-3');
        input.classList.add('form-control');
        input.placeholder = 'Введите название нового дела';
        buttonWrapper.classList.add('input-group-append');
        button.classList.add('btn', 'btn-primary');
        button.textContent = 'Добавить дело';
        button.disabled = true;

        buttonWrapper.append(button);
        form.append(input);
        form.append(buttonWrapper);

        input.addEventListener('input', function(e) {
            e.preventDefault();
            if (input.value.length > 0) {
                button.disabled = false;
            } else if (input.value.length == 0) {
                button.disabled = true;
            }
        });

        return {
            form,
            input,
            button,
        };
    }

    // Создаём и возвращаем список элементов
    function createTodoList() {
        let list = document.createElement('ul');
        list.classList.add('list-group');
        return list;
    }

    function createTodoItem(name, done) {
        let item = document.createElement('li');
        // Кнопки помещаем в элемент, который красиво покажет их в одной группе
        let buttonGroup = document.createElement('div');
        let doneButton = document.createElement('button');
        let deleteButton = document.createElement('button');

        // Устанавливаем стили для элемента сиписка, а так же для размещения кнопок
        // в его правой части с помощью flex
        item.classList.add('list-group-item', 'd-flex', 'justify-content-between', 'align-items-center');
        item.textContent = name;

        buttonGroup.classList.add('btn-group', 'btn-group-sm');
        doneButton.classList.add('btn', 'btn-success');
        doneButton.textContent = 'Готово';
        deleteButton.classList.add('btn', 'btn-danger');
        deleteButton.textContent = 'Удалить';

        // Вкладываем кнопки в отдельный элемент, чтобы они объединились в один блок
        buttonGroup.append(doneButton);
        buttonGroup.append(deleteButton);
        item.append(buttonGroup);

        if (done == true) {
            item.classList.add('list-group-item-success');
        }

        doneButton.addEventListener('click', function() {
            item.classList.toggle('list-group-item-success');
        });
        deleteButton.addEventListener('click', function() {
            if (confirm('Вы уверены?')) {
                item.remove();
            }
        });

        // Приложению нужен доступ к самому элементу и кнопкам, чтобы обрабатывать события нажатия
        return {
            item,
            doneButton,
            deleteButton,
        };
    }

    function createTodoApp(container, title = 'Список дел', arrayCases) {

        let todoAppTitle = createAppTitle(title);
        let todoItemForm = createTodoItemForm();
        let todoList = createTodoList();

        if (localStorage.getItem('todoItems') == null) {
            localStorage.setItem('todoItems', JSON.stringify(arrayCases));
        }

        todoItemsDefault = JSON.parse(localStorage.getItem('todoItems'));

        container.append(todoAppTitle);
        container.append(todoItemForm.form);
        container.append(todoList);

        for (let todoItem of todoItemsDefault) {

            let todoItemElem = createTodoItem(todoItem.name, todoItem.done);

            todoList.append(todoItemElem.item);

        }

        // Браузер создаёт событие submit на форме по нажатию на Enter или на кнопку создания дела
        todoItemForm.form.addEventListener('submit', function(e) {
            // Эта строчка необходима, чтобы предотвратить стандартное действие браузера
            // В данном случае мы не хотим, чтобы страница перезагружалась при отправке формы
            e.preventDefault();

            // Игнорируем создание элемента, если пользователь ничего не ввел в поле
            if (!todoItemForm.input.value) {
                return;
            }

            let todoItem = createTodoItem(todoItemForm.input.value);
            // Создаём и добавляем в список новое дело с названием из поля для ввода
            todoList.append(todoItem.item);
            // Обнуляем значение в поле, чтобы не пришлось стирать его вручную
            todoItemForm.input.value = '';
        });
    }

    window.createTodoApp = createTodoApp;

})();


Ответы (1 шт):

Автор решения: Андрей Стешенко

Всё добавленное описал в комментариях с звездочками. С радостью рассмотрю варианты как можно было сделать лучше.

(function () {
  // ***выносим сюда чтобы доступна была для всех функций***
  let todoItemsDefault = JSON.parse(localStorage.getItem("todoItems"));

  // Создаём и возвращаем заголовок приложения
  function createAppTitle(title) {
    let appTitle = document.createElement("h2");
    appTitle.innerHTML = title;
    return appTitle;
  }

  // Создаём и возвращаем форму для создания дела
  function createTodoItemForm() {
    let form = document.createElement("form");
    let input = document.createElement("input");
    let buttonWrapper = document.createElement("div");
    let button = document.createElement("button");

    form.classList.add("input-group", "mb-3");
    input.classList.add("form-control");
    input.placeholder = "Введите название нового дела";
    buttonWrapper.classList.add("input-group-append");
    button.classList.add("btn", "btn-primary");
    button.textContent = "Добавить дело";
    button.disabled = true;

    buttonWrapper.append(button);
    form.append(input);
    form.append(buttonWrapper);

    input.addEventListener("input", function (e) {
      e.preventDefault();
      if (input.value.length > 0) {
        button.disabled = false;
      } else if (input.value.length == 0) {
        button.disabled = true;
      }
    });

    return {
      form,
      input,
      button,
    };
  }

  // Создаём и возвращаем список элементов
  function createTodoList() {
    let list = document.createElement("ul");
    list.classList.add("list-group");
    return list;
  }

  // ***Записываем новое состояние приложения в localstorage***
  function setItLocal(newState) {
    localStorage.setItem("todoItems", JSON.stringify(newState));
  }

  function createTodoItem(name, done) {
    let item = document.createElement("li");
    // Кнопки помещаем в элемент, который красиво покажет их в одной группе
    let buttonGroup = document.createElement("div");
    let doneButton = document.createElement("button");
    let deleteButton = document.createElement("button");

    // Устанавливаем стили для элемента сиписка, а так же для размещения кнопок
    // в его правой части с помощью flex
    item.classList.add(
      "list-group-item",
      "d-flex",
      "justify-content-between",
      "align-items-center"
    );
    item.textContent = name;

    buttonGroup.classList.add("btn-group", "btn-group-sm");
    doneButton.classList.add("btn", "btn-success");
    doneButton.textContent = "Готово";
    // ***data атрибут укажет кому конкретно принадлежит нажатая кнопка***
    doneButton.dataset.name = name;
    deleteButton.classList.add("btn", "btn-danger");
    deleteButton.textContent = "Удалить";
    // ***data атрибут укажет кому конкретно принадлежит нажатая кнопка***
    deleteButton.dataset.name = name;

    // Вкладываем кнопки в отдельный элемент, чтобы они объединились в один блок
    buttonGroup.append(doneButton);
    buttonGroup.append(deleteButton);
    item.append(buttonGroup);

    if (done == true) {
      item.classList.add("list-group-item-success");
    }

    // ***помещаем обработчики событий в цикл и пробегаемся по объектам в массиве todoItemsDefault,***
    // ***благодаря data атрибуту мы знаем какой объект нужно перезаписать***
    for (let todoItem of todoItemsDefault) {
      doneButton.addEventListener("click", function () {
        if (todoItem.name == this.dataset.name) {
          item.classList.toggle("list-group-item-success");
          let index = todoItemsDefault.indexOf(todoItem);
          todoItemsDefault[index].done = todoItem.done ? false : true;
          setItLocal(todoItemsDefault);
        }
      });

      deleteButton.addEventListener("click", function () {
        if (todoItem.name == this.dataset.name && confirm("Вы уверены?")) {
          item.remove();
          todoItemsDefault.splice(todoItemsDefault.indexOf(todoItem), 1);
          setItLocal(todoItemsDefault);
        }
      });
    }

    // Приложению нужен доступ к самому элементу и кнопкам, чтобы обрабатывать события нажатия
    return {
      item,
      doneButton,
      deleteButton,
    };
  }

  function createTodoApp(container, title = "Список дел", arrayCases) {
    let todoAppTitle = createAppTitle(title);
    let todoItemForm = createTodoItemForm();
    let todoList = createTodoList();

    if (localStorage.getItem("todoItems") == null) {
      setItLocal(arrayCases);
    }

    container.append(todoAppTitle);
    container.append(todoItemForm.form);
    container.append(todoList);

    for (let todoItem of todoItemsDefault) {
      let todoItemElem = createTodoItem(todoItem.name, todoItem.done);

      todoList.append(todoItemElem.item);
    }

    // Браузер создаёт событие submit на форме по нажатию на Enter или на кнопку создания дела
    todoItemForm.form.addEventListener("submit", function (e) {
      // Эта строчка необходима, чтобы предотвратить стандартное действие браузера
      // В данном случае мы не хотим, чтобы страница перезагружалась при отправке формы
      e.preventDefault();

      // Игнорируем создание элемента, если пользователь ничего не ввел в поле
      if (!todoItemForm.input.value) {
        return;
      }

      // ***пушим новый объект с параметрами элемента в масив***
      todoItemsDefault.push({
        name: todoItemForm.input.value,
        done: false,
      });
      // ***записали в localstorge***
      setItLocal(todoItemsDefault);

      let todoItem = createTodoItem(todoItemForm.input.value);
      // Создаём и добавляем в список новое дело с названием из поля для ввода
      todoList.append(todoItem.item);
      // Обнуляем значение в поле, чтобы не пришлось стирать его вручную
      todoItemForm.input.value = "";
    });
  }

  window.createTodoApp = createTodoApp;
})();

→ Ссылка