Создание интерактивного списка задач (todo list)

Тренажер по работе с HTML DOM на JavaScript для пользователей с начальным и средним уровнем подготовки.

Тренажер JavaScript: Покоряем HTML DOM

Этот набор заданий поможет вам попрактиковаться в манипулировании HTML DOM с помощью JavaScript для создания классического приложения - списка задач (Todo List). Вы начнете с основ: выбора элементов и добавления новых задач. Затем перейдете к более сложным вещам, таким как отметка задач как выполненных и их удаление. Каждое задание предоставляет готовый HTML и частично написанный JavaScript код, который вам нужно будет дополнить, чтобы реализовать требуемую функциональность. Это отличный способ закрепить знания по работе с событиями, созданию и изменению элементов DOM.

Список тем

Выбор элементов для Todo List

id: 37086_js-dom-todo-1-select

Для начала работы с нашим списком задач нам нужно получить доступ к основным элементам на странице: полю для ввода новой задачи, кнопке для добавления задачи и списку, куда будут добавляться задачи. Ваша задача - используя JavaScript, найти эти три элемента по их ID и сохранить их в переменные.

HTML
Восстановить HTML
<input type="text" id="taskInput" placeholder="Новая задача...">
<button id="addButton">Добавить</button>
<ul id="taskList"></ul>
JavaScript
const taskInput = document.input__1("taskInput");
const addButton = document.getElementById(input__2);
const taskList = input__3.getElementById("taskList");

// Для проверки можешь вывести элементы в консоль (не обязательно)
console.log(taskInput, addButton, taskList);
Используйте метод `document.getElementById()` для поиска элемента по его уникальному идентификатору (атрибуту `id`). Передайте ID нужного элемента в качестве строки этому методу.
Заполнить ответами все поля
Результат
Лог
Выполнить
Показать подсказку

Добавление новой задачи в список

id: 37086_js-dom-todo-2-add

Теперь реализуем основную функцию: добавление новой задачи. При нажатии на кнопку 'Добавить' должен создаваться новый элемент списка (`<li>`), текст для него браться из поля ввода, и этот новый элемент должен добавляться в конец списка `<ul>`.

HTML
Восстановить HTML
<input type="text" id="taskInput" placeholder="Новая задача...">
<button id="addButton">Добавить</button>
<ul id="taskList"></ul>
JavaScript
const taskInput = document.getElementById("taskInput");
const addButton = document.getElementById("addButton");
const taskList = document.getElementById("taskList");

addButton.input__1("click", function() {
  const taskText = taskInput.input__2;

  // Создаем новый элемент списка
  const newTask = document.input__3("li");

  // Устанавливаем текст задачи
  newTask.input__4 = taskText;

  // Добавляем задачу в список
  taskList.input__5(newTask);

  // Очищаем поле ввода (добавим в следующем шаге)
  // taskInput.value = "";
});
Добавьте обработчик события 'click' на кнопку. Внутри обработчика получите значение поля ввода (`.value`). Создайте новый элемент `<li>` с помощью `document.createElement()`. Установите его текстовое содержимое (`.textContent`). Добавьте созданный элемент в `taskList` с помощью метода `.appendChild()`.
Заполнить ответами все поля
Результат
Лог
Выполнить
Показать подсказку

Очистка поля ввода и проверка на пустую задачу

id: 37086_js-dom-todo-3-clear-validate

Улучшим предыдущий шаг. Во-первых, после добавления задачи поле ввода должно автоматически очищаться. Во-вторых, не нужно добавлять пустые задачи. Добавьте проверку: если поле ввода пустое (или содержит только пробелы), задача не должна добавляться.

HTML
Восстановить HTML
<input type="text" id="taskInput" placeholder="Новая задача...">
<button id="addButton">Добавить</button>
<ul id="taskList"></ul>
JavaScript
const taskInput = document.getElementById("taskInput");
const addButton = document.getElementById("addButton");
const taskList = document.getElementById("taskList");

addButton.addEventListener("click", function() {
  const taskText = taskInput.value.input__1(); // Удаляем пробелы по краям

  // Проверяем, не пустая ли строка
  if (taskText input__2 "") {
    const newTask = document.createElement("li");
    newTask.textContent = taskText;
    taskList.appendChild(newTask);

    // Очищаем поле ввода
    taskInput.input__3 = input__4;
  }
});
Чтобы очистить поле ввода, установите его свойство `.value` в пустую строку (`''`). Для проверки на пустоту используйте условный оператор `if`. Получите значение поля ввода, удалите пробелы по краям с помощью метода `.trim()` и сравните результат с пустой строкой. Весь код создания и добавления `<li>` должен находиться внутри блока `if`.
Заполнить ответами все поля
Результат
Лог
Выполнить
Показать подсказку

Отметка задачи как выполненной

id: 37086_js-dom-todo-4-complete

Добавим интерактивности: при клике на любую задачу в списке она должна помечаться как выполненная (или наоборот, если уже была выполнена). Визуально это будет достигаться добавлением/удалением CSS-класса `completed`, который перечеркивает текст.

CSS
ul {
  list-style: none;
  padding: 0;
}
li {
  padding: 8px;
  border-bottom: 1px solid #eee;
  cursor: pointer;
}
li:hover {
  background-color: #f9f9f9;
}
.completed {
  text-decoration: line-through;
  color: #aaa;
}
HTML
Восстановить HTML
<input type="text" id="taskInput" placeholder="Новая задача...">
<button id="addButton">Добавить</button>
<ul id="taskList">
  <li>Пример задачи 1</li>
  <li>Пример задачи 2</li>
</ul>
JavaScript
const taskInput = document.getElementById("taskInput");
const addButton = document.getElementById("addButton");
const taskList = document.getElementById("taskList");

// Код добавления задачи (из предыдущего шага)
addButton.addEventListener("click", function() {
  const taskText = taskInput.value.trim();
  if (taskText !== "") {
    const newTask = document.createElement("li");
    newTask.textContent = taskText;
    taskList.appendChild(newTask);
    taskInput.value = "";
  }
});

// Обработчик для отметки выполнения
taskList.addEventListener(input__1, function(input__2) {
  // Проверяем, что клик был по элементу LI
  if (event.input__3.tagName === input__4) {
    // Переключаем класс 'completed'
    event.target.input__5.toggle("completed");
  }
});
Используйте делегирование событий. Добавьте один обработчик события 'click' на весь список `taskList`. Внутри обработчика проверьте, был ли клик сделан именно по элементу `<li>` (можно проверить `event.target.tagName`). Если да, то используйте `event.target.classList.toggle()` для добавления/удаления класса `completed`.
Заполнить ответами все поля
Результат
Лог
Выполнить
Показать подсказку

Добавление кнопки удаления для каждой задачи

id: 37086_js-dom-todo-5-add-delete-button

Каждая задача должна иметь кнопку для ее удаления. Измените код добавления новой задачи так, чтобы вместе с текстом задачи в `<li>` добавлялся элемент (например, `<span>` или `<button>`) с текстом 'X' или символом ×, который будет служить кнопкой удаления. Добавьте этой кнопке CSS-класс `delete-btn` для стилизации и дальнейшего использования.

CSS
ul {
  list-style: none;
  padding: 0;
}
li {
  padding: 8px;
  border-bottom: 1px solid #eee;
  display: flex; /* Для расположения текста и кнопки в строку */
  justify-content: space-between; /* Разнести текст и кнопку по краям */
  align-items: center;
}
.completed {
  text-decoration: line-through;
  color: #aaa;
}
.delete-btn {
  color: red;
  cursor: pointer;
  margin-left: 10px; /* Небольшой отступ слева */
  font-weight: bold;
}
.delete-btn:hover {
  color: darkred;
}
HTML
Восстановить HTML
<input type="text" id="taskInput" placeholder="Новая задача...">
<button id="addButton">Добавить</button>
<ul id="taskList"></ul>
JavaScript
const taskInput = document.getElementById("taskInput");
const addButton = document.getElementById("addButton");
const taskList = document.getElementById("taskList");

addButton.addEventListener("click", function() {
  const taskText = taskInput.value.trim();
  if (taskText !== "") {
    const newTask = document.createElement("li");
    newTask.textContent = taskText; // Устанавливаем основной текст задачи

    // Создаем кнопку удаления
    const deleteBtn = document.input__1("span"); 
    deleteBtn.textContent = input__2; // Текст кнопки
    deleteBtn.input__3.add(input__4); // Добавляем класс

    // Добавляем кнопку внутрь li
    newTask.input__5(deleteBtn);

    taskList.appendChild(newTask);
    taskInput.value = "";
  }
});

// Обработчик для отметки выполнения (оставляем из предыдущего шага)
taskList.addEventListener("click", function(event) {
  // Пока обрабатываем только клик по LI для выполнения
  if (event.target.tagName === "LI") {
     // Проверяем, чтобы клик не был по кнопке удаления внутри LI
     if (!event.target.querySelector('.delete-btn') || !event.target.querySelector('.delete-btn').contains(event.target)) {
        event.target.classList.toggle("completed");
     }
  }
});
Внутри обработчика добавления задачи, после создания `<li>` и установки `textContent`, создайте еще один элемент (например, `<span>`) с помощью `document.createElement()`. Установите его `textContent` в ' × ' (или 'X'). Добавьте ему класс `delete-btn` с помощью `classList.add()`. Затем добавьте этот `<span>` внутрь `<li>` с помощью `newTask.appendChild()`.
Заполнить ответами все поля
Результат
Лог
Выполнить
Показать подсказку

Реализация удаления задачи

id: 37086_js-dom-todo-6-delete

Завершающий шаг: сделаем кнопку удаления рабочей. Используя делегирование событий на списке `taskList`, определите, был ли клик по элементу с классом `delete-btn`. Если да, то нужно найти родительский элемент `<li>` этой кнопки и удалить его из DOM.

CSS
ul {
  list-style: none;
  padding: 0;
}
li {
  padding: 8px;
  border-bottom: 1px solid #eee;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
li span:first-child {
    cursor: pointer; /* Делаем текст задачи кликабельным для выполнения */
    flex-grow: 1; /* Позволяем тексту занимать доступное место */
    margin-right: 10px;
}
.completed span:first-child {
  text-decoration: line-through;
  color: #aaa;
}
.delete-btn {
  color: red;
  cursor: pointer;
  font-weight: bold;
  padding: 0 5px; /* Небольшие отступы для кнопки */
}
.delete-btn:hover {
  color: darkred;
}
HTML
Восстановить HTML
<!-- Изменим структуру LI, чтобы текст был в отдельном span для корректной работы клика --> 
<input type="text" id="taskInput" placeholder="Новая задача...">
<button id="addButton">Добавить</button>
<ul id="taskList">
  <li><span>Пример задачи 1</span><span class="delete-btn">×</span></li>
  <li><span>Пример задачи 2</span><span class="delete-btn">×</span></li>
</ul>
JavaScript
const taskInput = document.getElementById("taskInput");
const addButton = document.getElementById("addButton");
const taskList = document.getElementById("taskList");

// Код добавления задачи с кнопкой удаления
addButton.addEventListener("click", function() {
  const taskText = taskInput.value.trim();
  if (taskText !== "") {
    const newTask = document.createElement("li");
    
    // Создаем span для текста задачи
    const taskTextSpan = document.createElement('span');
    taskTextSpan.textContent = taskText;
    newTask.appendChild(taskTextSpan);

    // Создаем кнопку удаления
    const deleteBtn = document.createElement("span"); 
    deleteBtn.textContent = " × "; 
    deleteBtn.classList.add("delete-btn");
    newTask.appendChild(deleteBtn);

    taskList.appendChild(newTask);
    taskInput.value = "";
  }
});

// Общий обработчик кликов на списке
taskList.addEventListener("click", function(event) {
  // Проверка клика по кнопке удаления
  if (event.target.input__1.input__2("delete-btn")) {
    const taskItem = event.target.input__3; // Получаем родительский LI
    taskItem.input__4(); // Удаляем LI
  }
  // Проверка клика по тексту задачи для выполнения (кликаем на SPAN внутри LI)
  else if (event.target.tagName === "SPAN" && !event.target.classList.contains("delete-btn")) {
     // Применяем класс к родительскому LI
     event.target.parentElement.classList.toggle("completed");
  }
});
Дополните существующий обработчик клика на `taskList`. Добавьте проверку: если `event.target` имеет класс `delete-btn` (используйте `event.target.classList.contains()`), то получите его родительский элемент `<li>` (с помощью `event.target.parentElement`). Затем удалите этот родительский элемент из DOM, вызвав у него метод `.remove()`.
Заполнить ответами все поля
Результат
Лог
Выполнить
Показать подсказку
НайтиКурс.Ру