JavaScript
Практические задачи по JavaScript для начинающих: DOM, массивы и события
Эта подборка нужна для перехода от задач в консоли к маленьким интерфейсам. Здесь JavaScript уже не просто считает массив, а реагирует на действие пользователя: клик, ввод текста, выбор фильтра, отправку формы. Именно на этом этапе новичок начинает понимать, как код связан со страницей
Работайте в одном файле index.html или в связке index.html и script.js. Для каждой задачи сначала добейтесь результата на простых данных, затем добавьте один неудобный случай: пустой ввод, повтор, неизвестный фильтр или отсутствие элементов
По этой теме полезно отдельно посмотреть Идеи приложений на JavaScript для портфолио, чтобы расширить контекст и сравнить подходы
По этой теме полезно отдельно посмотреть Вопросы JavaScript на собеседовании, чтобы расширить контекст и сравнить подходы
Базовая HTML-разметка для задач
Можно начать с такой страницы и менять блоки под конкретные задания
<input id="taskInput" placeholder="Новая задача">
<button id="addTask">Добавить</button>
<ul id="taskList"></ul>
<p id="message"></p>
<script src="script.js"></script>
Главная идея: JavaScript берет элементы страницы, слушает событие и меняет DOM. Если один из трех шагов пропущен, интерфейс не будет работать
Задача 1. Добавить элемент в список
Пользователь вводит текст и нажимает кнопку. Нужно добавить новый пункт в список
const input = document.querySelector('#taskInput');
const button = document.querySelector('#addTask');
const list = document.querySelector('#taskList');
const message = document.querySelector('#message');
button.addEventListener('click', () => {
const title = input.value.trim();
if (!title) {
message.textContent = 'Введите текст задачи';
return;
}
const item = document.createElement('li');
item.textContent = title;
list.append(item);
input.value = '';
message.textContent = '';
});
Проверьте пустую строку и строку из пробелов. Без trim() такая задача часто ломается незаметно
Задача 2. Хранить задачи в массиве
Теперь храните данные отдельно от разметки. Это важный шаг: DOM показывает состояние, но источником состояния становится массив
const tasks = [];
function renderTasks() {
list.innerHTML = '';
tasks.forEach((task) => {
const item = document.createElement('li');
item.textContent = task.title;
list.append(item);
});
}
button.addEventListener('click', () => {
const title = input.value.trim();
if (!title) return;
tasks.push({ title, done: false });
input.value = '';
renderTasks();
});
Если список на странице стал неправильным, проверьте массив через console.log(tasks). Так проще понять, ошибка в данных или в выводе
Задача 3. Отметить задачу выполненной
Добавьте обработчик клика по пункту списка
function renderTasks() {
list.innerHTML = '';
tasks.forEach((task, index) => {
const item = document.createElement('li');
item.textContent = task.done ? `✓ ${task.title}` : task.title;
item.addEventListener('click', () => {
tasks[index].done = !tasks[index].done;
renderTasks();
});
list.append(item);
});
}
Эта задача показывает связь массива и DOM: клик меняет данные, затем интерфейс перерисовывается
Задача 4. Фильтр выполненных задач
Добавьте кнопки Все, Активные, Готовые. Храните текущий фильтр в переменной
let filter = 'all';
function getVisibleTasks() {
if (filter === 'active') {
return tasks.filter((task) => !task.done);
}
if (filter === 'done') {
return tasks.filter((task) => task.done);
}
return tasks;
}
В renderTasks() используйте getVisibleTasks(). Проверьте пустой список, список только из выполненных задач и переключение фильтра после добавления новой задачи
Задача 5. Поиск по списку
Добавьте поле поиска и показывайте только задачи, где есть введенный текст
const searchInput = document.querySelector('#search');
let query = '';
searchInput.addEventListener('input', () => {
query = searchInput.value.trim().toLowerCase();
renderTasks();
});
function getVisibleTasks() {
return tasks.filter((task) => {
const matchesQuery = task.title.toLowerCase().includes(query);
const matchesFilter = filter === 'all' || (filter === 'done' ? task.done : !task.done);
return matchesQuery && matchesFilter;
});
}
Проверьте регистр, пустой поиск и запрос без совпадений. Если ничего не найдено, лучше показать текст, а не пустую страницу
Задача 6. Сохранение в localStorage
Чтобы задачи не пропадали после перезагрузки, сохраните массив
function saveTasks() {
localStorage.setItem('tasks', JSON.stringify(tasks));
}
function loadTasks() {
const raw = localStorage.getItem('tasks');
return raw ? JSON.parse(raw) : [];
}
После каждого изменения вызывайте saveTasks(). При загрузке страницы берите начальное значение из loadTasks()
Частые ошибки
Первая ошибка — менять DOM и забывать менять массив. После перерисовки данные пропадают
Вторая ошибка — добавлять обработчики внутри цикла и путаться с индексами. Проверяйте, что клик меняет именно нужную задачу
Третья ошибка — хранить всё в innerHTML. Для учебного старта лучше создавать элементы через createElement, потому что так понятнее, где текст, где кнопка, где событие
Четвертая ошибка — не обрабатывать пустое состояние. Пользователь должен видеть «задач нет», а не просто пустой блок
Мини-проект для закрепления
Соберите todo-list с такими функциями:
- добавить задачу
- отметить задачу выполненной
- удалить задачу
- фильтровать по статусу
- искать по тексту
- сохранять в localStorage
Если эта версия работает, вы уже закрыли главные темы начального JavaScript: DOM, события, массивы, объекты, функции и хранение состояния
Задача 7. Удаление задачи
Добавьте кнопку удаления для каждого элемента. В массиве удобнее хранить id, чтобы удалять не по тексту, а по стабильному идентификатору
function removeTask(id) {
const index = tasks.findIndex((task) => task.id === id);
if (index !== -1) {
tasks.splice(index, 1);
saveTasks();
renderTasks();
}
}
В renderTasks() создайте кнопку, повесьте на нее обработчик и вызовите removeTask(task.id). Проверьте удаление первой, последней и единственной задачи
Задача 8. Счетчик задач
Покажите пользователю, сколько задач осталось выполнить
function getActiveCount() {
return tasks.filter((task) => !task.done).length;
}
function renderCounter() {
counter.textContent = `Осталось: ${getActiveCount()}`;
}
Вызывайте renderCounter() после добавления, удаления и переключения задачи. Так вы увидите, что интерфейс зависит от данных, а не живет отдельно
Как проверять весь мини-проект
Пройдите сценарий как обычный пользователь: добавьте задачу, добавьте пустую строку, отметьте задачу выполненной, включите фильтр, удалите задачу, перезагрузите страницу. Если после перезагрузки состояние сохранилось, localStorage подключен правильно
Если что-то ломается, откройте консоль и смотрите не только ошибку, но и текущий массив задач. В учебных DOM-проектах половина ошибок связана с тем, что данные и разметка перестали совпадать
Задача 9. Сообщение для пустого списка
Когда задач нет, покажите отдельное сообщение. Это маленькая деталь, но она делает интерфейс понятнее
function renderEmptyState() {
message.textContent = tasks.length === 0 ? 'Задач пока нет' : '';
}
Вызывайте эту функцию после renderTasks(). Проверьте три случая: список пустой, добавлена одна задача, все задачи удалены