Урок 6 из 8 · 25 мин · обновлено
Мини-приложение: логика, данные и проверка
Как довести трекер привычек до работающего приложения: сохранение отметок в браузере, счётчик дней, добавление привычек. Как проверять каждый шаг по сценариям и ловить пограничные случаи.
От страницы к приложению
Пока трекер — красивая страница: галочки ставятся, но после обновления пропадают. В этом уроке он станет приложением. План уже есть — из урока 3:
- Отметки сохраняются после обновления страницы.
- Счётчик дней рядом с каждой привычкой.
- Добавление своей привычки.
Делаем по одному шагу, проверяем, сохраняем копию.
Где живут данные
Чтобы отметки не пропадали, их нужно где-то хранить. У приложения в одном файле без сервера есть одно место — хранилище браузера (localStorage). Оно переживает обновление страницы и перезапуск браузера.
Но у него есть границы, о которых важно знать заранее:
- данные живут в одном браузере на одном устройстве: на телефоне список будет свой;
- если очистить данные браузера, отметки пропадут;
- для общего списка на нескольких устройствах нужен сервер и учётные записи — это уже проект другого уровня.
Для личного трекера этого достаточно. Важно, чтобы вы сами понимали эти рамки и не обещали другим людям то, чего приложение не умеет.
Проверка по сценариям
Как понять, что шаг сделан? Не «на вид работает», а по сценариям: действие и ожидаемый результат. Для трёх шагов трекера:
| Действие | Ожидаемый результат |
|---|---|
| Отметить «Зарядку», обновить страницу | галочка на месте, карточка зелёная |
| Снять галочку, обновить страницу | галочки нет, счётчик уменьшился |
| Отметить привычку | счётчик рядом стал на 1 больше |
| Ввести «Бег», нажать «Добавить» | появилась карточка «Бег» со счётчиком 0 |
| Обновить страницу после добавления | «Бег» на месте |
| Нажать «Добавить» с пустым полем | ничего не добавилось |
| Ввести только пробелы, нажать «Добавить» | ничего не добавилось |
Первые пять строк — обычные сценарии. Последние две — пограничные случаи: необычные, но возможные действия. Именно они ломаются чаще всего: нейросеть хорошо делает задуманный путь и легко забывает про странный.
Сценарии можно попросить у самой нейросети: «Составь таблицу проверок для этого шага: действие и ожидаемый результат, включая пограничные случаи». Но проходить их нужно руками — нейросеть ваш файл не запускает.
Шаг за шагом
Шаг 1 — сохранение. Запрос: «Сделай шаг 1 из плана: отметки сохраняются в хранилище браузера. Остальное не меняй. Пришли файл целиком». Проверьте первые две строки таблицы. Работает — сохраните копию tracker-2.html.
Шаг 2 — счётчик. Проверьте третью строку: отметили — счётчик вырос, сняли — уменьшился. Копия tracker-3.html.
Шаг 3 — добавление. Проверьте оставшиеся строки, особенно две последние. Если пробелы проходят, напишите: «Сейчас можно добавить привычку из одних пробелов — появляется пустая карточка. Не добавляй привычку, если после удаления пробелов по краям название пустое». Копия tracker-4.html.
Что ещё может пойти не так
Пара случаев, которые полезно проверить в любом приложении с данными:
- Очень длинное название. Влезает ли карточка в экран телефона, не ломается ли вёрстка?
- Повторное быстрое нажатие. Не появляются ли две одинаковые отметки за день?
- Новый день. Утром галочки должны быть сняты, а счётчики — сохраниться. Проверить можно завтра или попросить нейросеть временно добавить для проверки кнопку «Сменить день» — и убрать её после.
Попробуйте сами. Доведите трекер до работающего приложения: три шага плана, проверка каждого по таблице сценариев, копия после каждого шага. Если застряли, сравните свой файл с примером ниже.
Пример готового трекера
Ваш код почти наверняка будет выглядеть иначе — нейросети пишут по-разному, и это нормально. Важно, чтобы он проходил все сценарии из таблицы. Один из рабочих вариантов:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Трекер привычек</title>
<style>
body { font-family: system-ui, sans-serif; max-width: 480px; margin: 40px auto; padding: 0 16px; }
.habit { display: flex; align-items: center; gap: 12px; padding: 12px; margin-bottom: 8px; border: 1px solid #ddd; border-radius: 12px; }
.habit.done { background: #e8f5e9; border-color: #66bb6a; }
.habit .name { flex: 1; }
.habit .count { color: #777; font-size: 14px; }
form { display: flex; gap: 8px; margin-top: 16px; }
form input { flex: 1; padding: 8px; }
</style>
</head>
<body>
<h1>Мои привычки</h1>
<p id="today"></p>
<div id="list"></div>
<form id="add-form">
<input type="text" id="new-habit" placeholder="Новая привычка" required>
<button type="submit">Добавить</button>
</form>
<script>
// Сегодняшняя дата в виде 2026-10-20 — по местному времени
const now = new Date();
const today = now.getFullYear() + '-' + String(now.getMonth() + 1).padStart(2, '0') + '-' + String(now.getDate()).padStart(2, '0');
document.getElementById('today').textContent = 'Сегодня: ' + now.toLocaleDateString('ru-RU');
// Привычки хранятся в браузере: название и даты, когда привычка выполнена
let habits = JSON.parse(localStorage.getItem('habits')) || [
{ name: 'Зарядка', days: [] },
{ name: 'Читать 20 минут', days: [] },
{ name: 'Выпить 2 литра воды', days: [] }
];
function save() {
localStorage.setItem('habits', JSON.stringify(habits));
}
function render() {
const list = document.getElementById('list');
list.innerHTML = '';
habits.forEach((habit, index) => {
const row = document.createElement('label');
row.className = habit.days.includes(today) ? 'habit done' : 'habit';
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.checked = habit.days.includes(today);
checkbox.addEventListener('change', () => toggle(index));
const name = document.createElement('span');
name.className = 'name';
name.textContent = habit.name;
const count = document.createElement('span');
count.className = 'count';
count.textContent = 'дней: ' + habit.days.length;
row.append(checkbox, name, count);
list.append(row);
});
}
function toggle(index) {
const days = habits[index].days;
if (days.includes(today)) {
habits[index].days = days.filter(day => day !== today);
} else {
days.push(today);
}
save();
render();
}
document.getElementById('add-form').addEventListener('submit', event => {
event.preventDefault();
const input = document.getElementById('new-habit');
const name = input.value.trim();
if (!name) return;
habits.push({ name: name, days: [] });
input.value = '';
save();
render();
});
render();
</script>
</body>
</html>
На что обратить внимание:
- даты хранятся строками вида
2026-10-20по местному времени — так отметка не «переедет» на соседний день после полуночи; trim()убирает пробелы по краям, а проверкаif (!name) returnне даёт добавить пустую привычку;- название привычки выводится через
textContent— почему это важно, разберём в следующем уроке.
Мини-тест: проверьте себя
Закрепите урок: несколько вопросов, разбор ответов — в конце.
-
Где трекер хранит отметки?
localStorage — хранилище конкретного браузера на конкретном устройстве. На телефоне и в другом браузере список будет свой.
-
Что такое пограничный случай?
Обычный сценарий нейросеть почти всегда делает правильно. Ломаются именно необычные действия, поэтому их проверяют отдельно.
-
Вы добавили привычку из одних пробелов, и появилась пустая карточка. Что это?
Пробелы — не название. Правило из задания должно отсекать и пустую строку, и строку из одних пробелов.
-
Почему отметки с компьютера не видны на телефоне?
Хранилище браузера не синхронизируется между устройствами. Общие данные требуют сервера и учётных записей — это уже следующий уровень сложности.