Гид по технологиям

Как создать To‑Do список на JavaScript и DOM

5 min read Frontend Обновлено 17 Apr 2026
Создать To‑Do список на JavaScript и DOM
Создать To‑Do список на JavaScript и DOM

Обложка: создание To‑Do списка

TL;DR

Краткое пошаговое руководство по созданию простого To‑Do приложения на чистом JavaScript с манипуляцией DOM и хранением в localStorage. Показываю базовую разметку с TailwindCSS, обработку событий, добавление/редактирование/удаление задач, а также рекомендации по безопасности, тестам и альтернативным подходам.


Введение

DOM (Document Object Model) — это модель представления структуры и содержимого веб‑страницы в виде объектов. С помощью JavaScript вы можете получать доступ ко всем элементам DOM и динамически создавать, читать, обновлять и удалять (CRUD) их.

Эта статья объясняет, как выполнять CRUD‑операции для простого To‑Do списка, управляя DOM. Предполагается, что вы знакомы с основами HTML и JavaScript.

Краткое определение

  • DOM: представление HTML в виде объектов, доступных через window.document.

Понимание базовой работы с DOM

Разберём простой пример (исходный код сохранён):

Submit  
  

В этом примере переменная submitButton даёт доступ к HTML‑кнопке. Мы добавляем обработчик события click: когда пользователь кликает — появляется окно с текстом “The form has been submitted.”.

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

Улучшенный вариант с корректными кавычками и более чистым кодом:


Разметка и TailwindCSS

Ниже — HTML‑макет проекта. Поля и кнопки имеют id, чтобы получить к ним доступ из JavaScript.

Для стилизации в статье использован TailwindCSS с CDN:

  

Исходный HTML (сохранён для полноты):

     
       
         

           To-Do List App          

         
                                                         
       
       
     
   

Так выглядит приложение на этой стадии:

Внешний вид приложения To‑Do в процессе разработки

Получение элементов и хранение состояния

В JavaScript первым шагом мы получаем элементы по id и инициализируем массив задач:

const text = document.getElementById("text");  
const addTaskButton = document.getElementById("add-task-btn");  
const saveTaskButton = document.getElementById("save-todo-btn");  
const listBox = document.getElementById("listBox");  
const saveInd = document.getElementById("saveIndex");  

let todoArray = [];  

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

Добавление задач

При клике на кнопку “Add” задача добавляется в массив и сохраняется в localStorage. Исходный обработчик (сохранён):

addTaskButton.addEventListener("click", (e) => {  
 e.preventDefault();  
 let todo = localStorage.getItem("todo");  
 if (todo === null) {  
   todoArray = [];  
 } else {  
   todoArray = JSON.parse(todo);  
 }  
 todoArray.push(text.value);  
 text.value = "";  
 localStorage.setItem("todo", JSON.stringify(todoArray));  
 displayTodo();  
});  

Ключевые моменты:

  • Всегда валидируйте text.value (пустые строки, длину, пробелы).
  • localStorage синхронен и имеет ограничения по объёму (~5–10 МБ в большинстве браузеров).

Улучшение: проверка на пустую строку и триминг:

addTaskButton.addEventListener("click", (e) => {
  e.preventDefault();
  const value = text.value.trim();
  if (!value) return; // не добавляем пустые задачи
  const todo = JSON.parse(localStorage.getItem("todo") || "[]");
  todo.push(value);
  localStorage.setItem("todo", JSON.stringify(todo));
  text.value = "";
  displayTodo();
});

Отображение списка

Функция displayTodo собирает HTML‑код и вставляет его в listBox через innerHTML (как в исходнике):

function displayTodo() {  
 let todo = localStorage.getItem("todo");  
 if (todo === null) {  
   todoArray = [];  
 } else {  
   todoArray = JSON.parse(todo);  
 }  
 let htmlCode = "";  
 todoArray.forEach((list, ind) => {  
   htmlCode += `
   

${list}

       
`;  });  listBox.innerHTML = htmlCode; }

Важно: вставка пользовательских данных в innerHTML без очистки может привести к XSS‑у. См. раздел безопасность ниже.

Удаление задач

Исходная реализация удаления использует splice и перезаписывает localStorage:

function deleteTodo(ind) {  
 let todo = localStorage.getItem("todo");  
 todoArray = JSON.parse(todo);  
 todoArray.splice(ind, 1);  
 localStorage.setItem("todo", JSON.stringify(todoArray));  
 displayTodo();  
}  

Список задач To‑Do с элементами и кнопками редактирования/удаления

Редактирование задач

Кнопка редактирования заполняет поле ввода значением задачи и переключает видимость кнопок “Add” и “Save”:

function edit(ind) {  
 saveInd.value = ind;  
 let todo = localStorage.getItem("todo");  
 todoArray = JSON.parse(todo);  
 text.value = todoArray[ind];  
 addTaskButton.style.display = "none";  
 saveTaskButton.style.display = "block";  
}  

После редактирования сохраняем изменения:

saveTaskButton.addEventListener("click", () => {  
 let todo = localStorage.getItem("todo");  
 todoArray = JSON.parse(todo);  
 let id = saveInd.value;  
 todoArray[id] = text.value;  
 addTaskButton.style.display = "block";  
 saveTaskButton.style.display = "none";  
 text.value = "";  
 localStorage.setItem("todo", JSON.stringify(todoArray));  
 displayTodo();  
});  

Форма добавления задачи в To‑Do

Редактирование существующей задачи To‑Do

Прогресс: список задач после изменений

Безопасность и устойчивость к ошибкам

Important: использование innerHTML с пользовательскими данными небезопасно. Возможные риски и mitigations:

  • Риск: XSS — пользователь может вставить HTML/скрипт как задачу.
    • mitigations: использовать textContent вместо innerHTML при вставке содержимого задач; явная очистка (sanitize) входных данных; ограничение длины.
  • Риск: конфликт индексов — если одновременно несколько вкладок изменяют localStorage, возможна рассинхронизация.
    • mitigations: подписка на событие window.addEventListener(‘storage’, …) для синхронизации между вкладками.
  • Ограничения localStorage: объём и синхронный API. Для больших или сложных приложений выбирайте IndexedDB.

Пример безопасного рендера с createElement (без innerHTML):

function safeDisplayTodo() {
  const todo = JSON.parse(localStorage.getItem('todo') || '[]');
  listBox.innerHTML = '';
  todo.forEach((item, index) => {
    const row = document.createElement('div');
    row.className = 'flex mb-4 items-center';

    const p = document.createElement('p');
    p.className = 'w-full text-grey-darkest';
    p.textContent = item; // безопасно

    const editBtn = document.createElement('button');
    editBtn.textContent = 'Edit';
    editBtn.className = '...';
    editBtn.addEventListener('click', () => edit(index));

    const delBtn = document.createElement('button');
    delBtn.textContent = 'Delete';
    delBtn.className = '...';
    delBtn.addEventListener('click', () => deleteTodo(index));

    row.appendChild(p);
    row.appendChild(editBtn);
    row.appendChild(delBtn);
    listBox.appendChild(row);
  });
}

Альтернативные подходы

  • Использовать фреймворки: React, Vue, Svelte — помогут упростить управление состоянием и рендеринг.
  • Хранение данных: localStorage (простой), IndexedDB (асинхронный, для больших объёмов), серверное хранилище (API + база данных) для синхронизации между устройствами.
  • Стили: Tailwind (утилитарный), Bootstrap (компоненты) — выбор зависит от предпочтений и команды.

Тестирование и критерии приёмки

Критерии приёмки

  • Пользователь может добавить новую задачу; она появляется в списке и сохраняется после перезагрузки.
  • Пользователь может отредактировать задачу; изменения сохраняются и видны сразу.
  • Пользователь может удалить задачу; элемент исчезает и больше не сохраняется.
  • Введённые данные отображаются безопасно (нет выполнения скриптов).

Примеры тест‑кейсов

  • Добавление: ввести “Купить хлеб”, нажать Add, проверить наличие задачи и localStorage.
  • Пустая строка: попытка добавить строку из пробелов — задача не создаётся.
  • Редактирование: изменить задачу и проверить изменение в localStorage.
  • Удаление: удалить задачу и убедиться, что index смещается корректно.

Руководство для ролей (коротко)

Developer

  • Реализовать валидацию, использовать textContent либо sanitize.
  • Обработать событие storage для мультивкладочной синхронизации.

QA

  • Проверить граничные значения, XSS‑вставки, поведение при переполнении localStorage.

Designer

  • Проверить доступность (контраст, размеры кнопок), фокус‑стейты и адаптивность.

Ментальные модели и эвристики

  • «Single source of truth»: храните состояние в одном месте (например, в localStorage или в state фреймворка).
  • «Render as data»: сначала подготовьте структуру данных, затем рендерьте DOM на её основе (не наоборот).
  • Предпочитайте делегирование событий при большом количестве элементов вместо назначения обработчика каждому элементу.

Быстрый чек‑лист по улучшению приложения

  • Валидация ввода (trim, maxlength).
  • Защита от XSS (textContent / sanitize).
  • Обработка storage для синхронизации вкладок.
  • Тесты для добавления/редактирования/удаления.
  • Подумать о миграции на IndexedDB или сервер для синхронизации между устройствами.

Decision flowchart

flowchart TD
  A[Начало] --> B{Есть localStorage?}
  B -- Да --> C[Загрузить массив задач]
  B -- Нет --> D[Создать пустой массив]
  C --> E[Показать список]
  D --> E
  E --> F{Действие пользователя}
  F -- Добавить --> G[Валидация и push]
  F -- Редактировать --> H[Поставить в input и переключить кнопки]
  F -- Удалить --> I[splice и сохранить]
  G --> J[Сохранить в localStorage]
  H --> J
  I --> J
  J --> E

Совместимость и миграция

  • localStorage доступен в большинстве современных браузеров, но имеет лимит и синхронный API.
  • Для офлайн‑первых и больших объёмов используйте IndexedDB (асинхронно).
  • Для многопользовательских приложений храните задачи на сервере (REST/GraphQL + БД).

Короткое объявление (100–200 слов)

Новый учебный проект: простое To‑Do приложение на чистом JavaScript и DOM. В статье разобраны все базовые операции: добавление, редактирование, удаление задач и сохранение состояния в localStorage. Также показаны практические улучшения — валидация ввода, безопасный рендер без innerHTML, обработка мультивкладочной синхронизации и рекомендации по миграции на IndexedDB или серверное хранилище. Полезно как для начинающих, так и для тех, кто хочет освежить подходы к безопасности и архитектуре в небольших фронтенд‑проектах.

Итог

  • To‑Do приложение — отличный учебный проект: покрывает DOM, события, хранение состояния и UX‑паттерны.
  • Обратите особое внимание на безопасность: избегайте вставки пользовательского HTML через innerHTML.
  • При росте проекта рассматривайте IndexedDB или серверную синхронизацию.

Спасибо за чтение — начинайте с малого, затем расширяйте функционал: отметки выполнения, фильтры, приоритеты и синхронизация между устройствами.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Gmail и настольные клиенты: выбор и настройка
Почта

Gmail и настольные клиенты: выбор и настройка

SketchUp бесплатно: как начать 3D‑моделирование
3D моделирование

SketchUp бесплатно: как начать 3D‑моделирование

Как создать аккаунт PlayStation Network (PSN)
Руководство

Как создать аккаунт PlayStation Network (PSN)

Почему iPhone и iPad нагреваются и как это исправить
Мобильные устройства

Почему iPhone и iPad нагреваются и как это исправить

Как искать жильё на Airbnb для отпуска
Путешествия

Как искать жильё на Airbnb для отпуска

Arduino Pong: ретро-игра на TV
Arduino

Arduino Pong: ретро-игра на TV