Практика №9. CSR с гидратацией. Интерактивный счётчик

Общее описание

В рамках этого задания вам необходимо создать веб-приложение, использующее Client-Side Rendering (CSR) с последующей гидратацией. Сервер отдаёт минимальный HTML-шаблон без содержимого — только контейнер и скрипт. Всё поведение (счётчик, управление значениями, стили) реализуется исключительно на клиенте через JavaScript.

Приложение — интерактивный счётчик с возможностью задавать начальное значение, минимальное и максимальное значение через атрибуты HTML. Сервер не участвует в логике — он только отдаёт статический index.html и app.js.

Задание обязано быть опубликовано в новом приватном репозитории организации GitHub с названием Группа_Фамилия_task-9.

Ресурсы

index.html

<!DOCTYPE html>
<html lang="ru">
<head>
  <meta charset="utf-8">
  <title>Счётчик</title>
  <link rel="stylesheet" href="/style.css">
</head>
<body>
  <div id="counter" 
       data-initial="0" 
       data-min="-5" 
       data-max="10">
  </div>
  <script src="/app.js"></script>
</body>
</html>

style.css

body {
  font-family: sans-serif;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  margin: 0;
  background-color: #f5f5f5;
}

#counter {
  text-align: center;
  padding: 2rem;
  border-radius: 12px;
  background-color: white;
  box-shadow: 0 4px 12px rgba(0,0,0,0.1);
  min-width: 200px;
}

#counter button {
  padding: 0.5rem 1rem;
  margin: 0 0.5rem;
  font-size: 1rem;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  transition: background-color 0.2s;
}

#counter button.decrease {
  background-color: #ff6b6b;
  color: white;
}

#counter button.reset {
  background-color: #f7b731;
  color: white;
}

#counter button.increase {
  background-color: #4ecdc4;
  color: white;
}

#counter span {
  font-size: 2rem;
  font-weight: bold;
  margin: 1rem 0;
  display: block;
}

#counter button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

Техническое задание

1. Серверная часть (NodeJS)

Создайте HTTP-сервер на NodeJS, который:

  • Отвечает на запрос GET / — отдаёт файл index.html
  • Отвечает на запрос GET /app.js — отдаёт файл app.js
  • Для всех остальных путей — отдаёт 404 с сообщением <h1>404 Not Found</h1>
  • Использует только встроенные модули: http, path, fs
  • Не использует readFileSync — только readFile
  • Устанавливает правильный Content-Type:
    • text/html для /
    • text/javascript для /app.js
  • Сервер слушает порт 3000

2. HTML-шаблон (index.html)

Создайте файл index.html с минимальной структурой:

  • Используйте <div id="counter"> как контейнер для счётчика
  • Добавьте три data-атрибута:
    • data-initial — начальное значение (по умолчанию 0)
    • data-min — минимальное допустимое значение (по умолчанию -5)
    • data-max — максимальное допустимое значение (по умолчанию 10)
  • Подключите стили из /style.css
  • Подключите скрипт /app.js в конце <body>

3. Стили (style.css)

Создайте файл style.css с базовыми стилями:

  • Стили должны применяться только через классы — не через inline-стили
  • Кнопки должны иметь цвета и переходы
  • Счётчик должен быть центрирован на странице

4. Клиентский скрипт (app.js)

Создайте файл app.js, который:

  • Находит элемент #counter
  • Считывает значения из data-initial, data-min, data-max (преобразует в числа)
  • Если атрибуты не заданы — использует значения по умолчанию: initial=0, min=-5, max=10
  • Генерирует внутри #counter:
    • <span> с текущим значением (изначально = initial)
    • Три кнопки: «Уменьшить», «Сбросить», «Увеличить»
  • При нажатии:
    • «Уменьшить» — уменьшает значение на 1, но не ниже min
    • «Увеличить» — увеличивает значение на 1, но не выше max
    • «Сбросить» — возвращает значение к initial
  • Кнопки «Уменьшить» и «Увеличить» должны отключаться, если достигнуты границы:
    • min → кнопка «Уменьшить» → disabled
    • max → кнопка «Увеличить» → disabled
  • Все изменения отображаются мгновенно — без перезагрузки
  • Никаких внешних библиотек — только чистый JavaScript (ES5+)
  • Никаких fetch(), innerHTML.

5. Пример работы

После загрузки страницы:

  • В HTML: <div id="counter" data-initial="0" data-min="-5" data-max="10"></div>
  • После выполнения app.js:
    <div id="counter" data-initial="0" data-min="-5" data-max="10">
      <span>0</span>
      <button class="decrease">Уменьшить</button>
      <button class="reset">Сбросить</button>
      <button class="increase">Увеличить</button>
    </div>
    
  • При нажатии «Увеличить» 12 раз — значение станет 10, кнопка «Увеличить» станет disabled
  • При нажатии «Уменьшить» — значение упадёт до -5, кнопка «Уменьшить» станет disabled
  • При нажатии «Сбросить» — значение вернётся к 0, кнопки снова активны

6. Технические требования

  • Сервер отдаёт только index.html, app.js и style.css — никаких других файлов
  • Все логика — на клиенте
  • Никаких React, Vue, jQuery — только чистый JS
  • Скрипт должен работать сразу после загрузки — без DOMContentLoaded (можно использовать defer или просто поместить в конец <body>)
  • Стили — только в style.css, не в JS
  • Никаких alert(), console.log() в финальной версии

Время на выполнение

На выполнение выделяется 4 практических пары.
Градация баллов после дедлайна:

  • 1 пара: 50%
  • 2 пара: 25%
  • 3 пара: 0%

Баллы

Статус выполнения Количество баллов
Не сдано -4
Сдано 8