Этап 2: Загрузка и рендеринг данных

Free Preview
Продолжительность:

Каркас готов. Теперь подключаем реальные данные: загружаем достопримечательности с сервера и отрисовываем карточки из JavaScript.

Требования

Загрузка данных

  • При загрузке страницы делайте запрос к https://advanced-frontend.ru/api/frontend-basics/attractions.
  • Пока данные грузятся, в сетке должно отображаться состояние загрузки. Минимум — текст «Загружаем...». Хорошо — скелетон-карточки (серые блоки той же формы, что настоящие карточки).
  • Если запрос завершился ошибкой, показывайте сообщение об ошибке и кнопку «Повторить», которая запускает запрос заново.
  • Карточки-заглушки из предыдущего этапа удалите из HTML — теперь весь контент генерируется из JavaScript.

Скелетон-карточки. Вместо текста «Загружаем...» можно вставить несколько заглушек-карточек без содержимого — просто серые прямоугольники тех же размеров с пульсирующей анимацией. Добавьте несколько <div class="card card--skeleton"></div> через JavaScript перед запросом и стилизуйте их через CSS:

.card--skeleton .card__image-wrap {
  background: #e0e0e0;
  animation: pulse 1.5s ease-in-out infinite;
}

@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.4; }
}
css

Кэширование

  • После успешного получения данных сохраните их в localStorage.
  • При следующей загрузке страницы проверяйте кэш: если данные есть и им менее 10 минут — используйте кэш, не делая запрос к серверу.
  • Если кэш устарел — делайте новый запрос и обновляйте кэш.

Структура кэша в localStorage. Храните объект с двумя полями: сами данные и временная метка:

// Сохраняем
localStorage.setItem('attractions', JSON.stringify({
  data: attractions,
  timestamp: Date.now(),
}));

// Читаем и проверяем
const cached = localStorage.getItem('attractions');
if (cached) {
  const { data, timestamp } = JSON.parse(cached);
  const TEN_MINUTES = 10 * 60 * 1000;
  if (Date.now() - timestamp < TEN_MINUTES) {
    // используем data
  }
}
js

Рендеринг карточек

  • Напишите функцию, которая принимает объект достопримечательности и возвращает готовый DOM-элемент карточки.
  • Добавляйте все карточки в DOM одним вызовом — через DocumentFragment.
  • Кнопки фильтрации по категориям тоже должны генерироваться из данных, а не быть захардкожены в HTML. Соберите уникальные категории из загруженного массива и создайте кнопку для каждой из них, плюс кнопку «Все».

Как собрать уникальные категории. Объект Set автоматически убирает дубликаты:

const categories = ['Все', ...new Set(attractions.map(a => a.category))];
js

Дальше проходитесь по categories и создавайте кнопку для каждого элемента.

Функция создания карточки. Держите её отдельно и чистой — принимает объект, возвращает элемент. Это позволит вызывать её и при первой загрузке, и позже:

function createCard(attraction) {
  const card = document.createElement('article');
  card.classList.add('card');
  card.dataset.id = attraction.id;
  card.innerHTML = `...`; // ваша разметка из этапа 1
  return card;
}

// Добавляем все карточки одним вызовом
const fragment = document.createDocumentFragment();
attractions.forEach(a => fragment.append(createCard(a)));
grid.replaceChildren(fragment);
js

Метод replaceChildren — удобная альтернатива innerHTML = '' + append: он очищает контейнер и вставляет новое содержимое за один вызов.

Хранение состояния

  • Держите полный массив загруженных достопримечательностей в переменной — он понадобится для фильтрации на следующем этапе.

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

const state = {
  attractions: [],      // все загруженные данные
  activeCategory: 'Все', // активный фильтр
  searchQuery: '',      // текущий поиск
  favourites: [],       // id избранных (этап 5)
};
js

Это проще, чем разбрасывать переменные по всему файлу, и легче расширять.

Продвинутое задание — IntersectionObserver

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

Для этого используется IntersectionObserver — браузерный API, который сообщает когда элемент появляется в области видимости. Изучите его самостоятельно:

Алгоритм: карточки изначально невидимы (opacity: 0), IntersectionObserver добавляет класс visible когда карточка входит в viewport — CSS-переход делает её видимой.

Ожидаемый результат

  • при открытии страницы виден индикатор загрузки
  • карточки появляются после загрузки данных с сервера
  • при повторной загрузке страницы данные берутся из кэша (проверьте во вкладке Network)
  • кнопки фильтрации создаются динамически из полученных данных
  • при сетевой ошибке видно сообщение с кнопкой «Повторить»