Капстон: фильтрация

Основы фронтенд разработки

Капстон: фильтрация

Продолжительность: 30 мин

Карточки загружаются и отображаются. Теперь добавим возможность искать и фильтровать — пользователь должен быстро найти то, что ищет.

Требования

Фильтрация по категории

  • Клик по кнопке категории показывает только карточки с соответствующей категорией.
  • Кнопка «Все» сбрасывает фильтр и показывает все карточки.
  • Активная кнопка визуально выделена (класс, который вы уже стилизовали в предыдущем этапе).
  • В один момент времени активна только одна кнопка категории.

Как переключать активную кнопку. Снимите класс у всех кнопок, затем добавьте нужной:

document.querySelectorAll('.filter-btn').forEach(btn => btn.classList.remove('active')); clickedButton.classList.add('active');
js

Поиск по тексту

  • Ввод текста в строку поиска фильтрует карточки по полям name и description.
  • Поиск нечувствителен к регистру.
  • Поиск работает одновременно с фильтром категории: если выбрана категория «Музей» и введён текст «эрм», показываются только музеи, в названии или описании которых есть «эрм».
  • При очистке поля поиска карточки возвращаются к предыдущему состоянию (с учётом активного фильтра категории).

Состояние «ничего не найдено»

  • Если ни одна карточка не подходит под текущие фильтры, в сетке должно отображаться сообщение «Ничего не найдено» с подсказкой сбросить поиск.
  • Рядом с сообщением — кнопка «Сбросить», которая очищает поле поиска и снимает фильтр категории.

Архитектура

  • Не удаляйте и не добавляйте карточки при каждом изменении фильтра. Лучший подход — держать все карточки в DOM и управлять их видимостью через CSS. Это быстрее и проще.
  • Вся логика фильтрации — одна функция, которая вызывается и при смене категории, и при вводе в поиск. Не дублируйте код.

Как связать всё вместе. Центральная идея — одна функция applyFilters, которая читает текущее состояние и решает, какие карточки показать:

function applyFilters() { const query = state.searchQuery.toLowerCase(); const category = state.activeCategory; document.querySelectorAll('.card').forEach(card => { const id = Number(card.dataset.id); const attraction = state.attractions.find(a => a.id === id); const matchesCategory = category === 'Все' || attraction.category === category; const matchesSearch = !query || attraction.name.toLowerCase().includes(query) || attraction.description.toLowerCase().includes(query); card.hidden = !(matchesCategory && matchesSearch); }); // показать/скрыть состояние "ничего не найдено" const anyVisible = [...document.querySelectorAll('.card')].some(c => !c.hidden); emptyState.hidden = anyVisible; }
js

Обработчики событий только обновляют state и вызывают applyFilters():

searchInput.addEventListener('input', (e) => { state.searchQuery = e.target.value; applyFilters(); }); filtersContainer.addEventListener('click', (e) => { const btn = e.target.closest('.filter-btn'); if (!btn) return; state.activeCategory = btn.dataset.category; // обновить активную кнопку applyFilters(); });
js

Обратите внимание на btn.dataset.category — при создании кнопок фильтрации добавляйте атрибут data-category="Музей", чтобы потом легко его читать.

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

Поиск запускается при каждом нажатии клавиши. Если данных много, это может тормозить браузер. Реальное решение — debounce: функция-обёртка, которая откладывает выполнение до тех пор, пока пользователь не перестанет печатать (обычно 300–400 мс).

Реализуйте debounce самостоятельно — это небольшая функция, которую полезно уметь писать:

Подсказка. debounce принимает функцию и задержку, возвращает новую функцию. Каждый вызов новой функции сбрасывает предыдущий таймер и запускает новый. Выполнение происходит только когда таймер «добежал» до конца.

function debounce(fn, delay) { let timer; return function(...args) { clearTimeout(timer); timer = setTimeout(() => fn(...args), delay); }; } // Использование: searchInput.addEventListener('input', debounce((e) => { state.searchQuery = e.target.value; applyFilters(); }, 300));
js

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

  • клик по категории показывает только карточки этой категории
  • поиск работает по имени и описанию, нечувствителен к регистру
  • поиск и фильтр по категории работают одновременно
  • при отсутствии результатов видно сообщение и кнопка сброса
  • активная кнопка категории визуально выделена

Это платный урок

Выберите тариф, чтобы открыть полный доступ к курсу

Самостоятельный

Учитесь в своём темпе. Всё, что нужно, чтобы освоить основы.

4 9007 900
Скидка 38%
  • Доступ ко всем урокам навсегда
  • Практические задания с автоматической проверкой кода в SourceCraft
  • Два проекта для портфолио
  • Первый модуль — бесплатно, без регистрации
  • Возврат денег в течение 14 дней
Популярный

С поддержкой

Личное код-ревью и прямая связь с автором, чтобы не застрять.

10 90013 900
Скидка 22%
  • Всё из тарифа «Самостоятельный»
  • Личное код-ревью ваших заданий от автора
  • Чат с автором в Telegram или MAX — задавайте любые вопросы
  • Одна персональная видеовстреча 30 минут

Премиум

Максимальное сопровождение: разбор кода вживую и еженедельные созвоны.

25 00029 000
Скидка 14%
  • Всё из тарифа «С поддержкой»
  • Живое код-ревью в реальном времени
  • Персональная видеовстреча 30 минут каждую неделю — 2 месяца (8 встреч)
  • Чат с автором в Telegram или MAX на всё время обучения
Безопасная оплата через ЮKassa · возврат в течение 14 дней

Комментарии

Войдите, чтобы оставить комментарий