Tilda · 7 мин чтения

Фильтр на Тильде без новых страниц: пошаговая инструкция

Фильтр на Тильде без новых страниц собираю обычно за один вечер: размечаю карточки атрибутами, вешаю JS-обработчик на клики по кнопкам фильтра и скрываю ненужные блоки через CSS-класс. Клиенты почти всегда сначала просят «сделать страницу под каждую категорию», и я объясняю, почему такой подход портит SEO и создаёт лишнюю работу на поддержку, а потом показываю рабочий пример на 30-40 карточек, который переключается без перезагрузки. Дальше разберу всю механику: от разметки в редакторе Tilda до готового скрипта с фильтрацией по нескольким параметрам и синхронизацией с адресной строкой.

Почему страницы под каждую категорию - плохая идея

Первое, что предлагает большинство гайдов по Tilda: сделать отдельный лендинг под каждый фильтр - «/catalog-divany», «/catalog-kresla», «/catalog-stoly». На вид просто, но на практике это превращается в боль уже на второй-третьей странице. Приходится вручную дублировать шапку, футер, блоки с контактами и синхронизировать изменения ассортимента сразу в десяти местах. Я делал такой сайт для магазина мебели на 42 страницы категорий, и клиент раз в две недели присылал правки, которые нужно было вносить в каждую копию отдельно - это часы рутины, которую легко исключить.

С точки зрения SEO дублирующиеся страницы с почти одинаковым контентом размывают вес между собой и путают поисковик: он не понимает, какую из десяти «диванных» страниц показывать в выдаче по запросу «купить диван». Плюс к этому у каждой лишней страницы своя скорость загрузки и свои метатеги, которые тоже нужно поддерживать руками. Фильтр без новых страниц решает это одним скриптом на одной странице: контент физически один, меняется только то, что видно пользователю.

Что умеет фильтровать Tilda «из коробки»

Встроенный фильтр в Tilda есть только в блоках интернет-магазина (T402, T403, T‑Store) и работает исключительно с товарами из каталога Tilda: можно фильтровать по цене, категории и текстовым меткам, заданным в карточке товара. Для обычного контента - портфолио, услуг, статей, объектов недвижимости - штатного фильтра нет вообще, такие блоки собираются вручную из Zero Block или готовых T‑блоков без фильтрации.

Второе ограничение: даже в блоках магазина фильтр не умеет комбинировать критерии, которых нет в стандартной схеме товара. Если нужно фильтровать по параметру «материал» и «цвет» одновременно, а не только «цена + категория», штатных настроек уже не хватает - приходится либо городить костыли через теги, либо писать свой скрипт поверх верстки. На практике я почти всегда ухожу во второй вариант, потому что он не привязан к типу блока и работает одинаково что на карточках товаров, что на портфолио, что на списке услуг.

Разметка карточек под кастомный фильтр

Первый шаг - разметить карточки данными, по которым скрипт будет их прятать и показывать. В Zero Block у каждого элемента есть поле дополнительных настроек, куда добавляется произвольный HTML-атрибут через раздел «Ещё» -> «Атрибуты» в панели редактирования блока. Для карточки товара или услуги добавляю что-то вроде:

<div class="filter-card" data-category="divany" data-price="25000">
  ...содержимое карточки...
</div>

Если верстаю блок через встроенный редактор Tilda, а не через кастомный HTML, тот же результат даёт CSS-класс с закодированным значением: например, класс filter-divany вешается на обёртку карточки через поле «доп. класс» в настройках элемента, а скрипт потом ищет по этому классу. Способ с data-атрибутами удобнее, когда параметров фильтрации больше двух-трёх - не приходится городить десятки классов на одну карточку.

Кнопки самого фильтра размечаю похожим образом - каждая кнопка получает data-filter со значением категории, а кнопка «Все» - пустое значение или data-filter=“all”.

Бесплатный материал

🎁 Полезный скрипт в подарок

Подпишитесь на Telegram - пришлю готовый скрипт по этой теме.

Без спама. Отписка в 1 клик.

JS-скрипт: фильтрация без перезагрузки страницы

Логика простая: по клику на кнопку фильтра скрипт проходит по всем карточкам, сравнивает их data-атрибут с выбранным значением и переключает видимость через CSS-класс, а не через display в inline-стиле - так проще управлять анимацией показа-скрытия. Вставляю скрипт через блок T123 (HTML-код) в конец страницы или в настройках страницы, в поле «Перед закрывающим тегом body».

document.addEventListener('DOMContentLoaded', function () {
  var buttons = document.querySelectorAll('[data-filter]');
  var cards = document.querySelectorAll('.filter-card');

  buttons.forEach(function (btn) {
    btn.addEventListener('click', function () {
      var value = btn.getAttribute('data-filter');

      buttons.forEach(function (b) { b.classList.remove('is-active'); });
      btn.classList.add('is-active');

      cards.forEach(function (card) {
        var match = value === 'all' || card.getAttribute('data-category') === value;
        card.classList.toggle('is-hidden', !match);
      });
    });
  });
});

Класс is-hidden добавляю через тот же блок T123, но в CSS-часть: display: none в паре с transition для плавности, если нужна анимация. Проверяю сразу на мобильной версии - Tilda для мобильного макета часто дублирует блок с другой версткой, и если фильтровать нужно и там, класс .filter-card должен присутствовать в обеих версиях разметки, иначе скрипт отработает только на десктопе.

Фильтр сразу по нескольким параметрам

Когда нужно фильтровать не по одной категории, а сразу по нескольким признакам - например, «диваны» и «цена до 30 000» одновременно, - логику меняю на накопление активных фильтров в объекте, а не на одно значение:

var activeFilters = { category: 'all', price: 'all' };

function applyFilters() {
  document.querySelectorAll('.filter-card').forEach(function (card) {
    var byCategory = activeFilters.category === 'all' ||
      card.getAttribute('data-category') === activeFilters.category;
    var byPrice = activeFilters.price === 'all' ||
      card.getAttribute('data-price-range') === activeFilters.price;

    card.classList.toggle('is-hidden', !(byCategory && byPrice));
  });
}

document.querySelectorAll('[data-group="category"]').forEach(function (btn) {
  btn.addEventListener('click', function () {
    activeFilters.category = btn.getAttribute('data-filter');
    applyFilters();
  });
});

document.querySelectorAll('[data-group="price"]').forEach(function (btn) {
  btn.addEventListener('click', function () {
    activeFilters.price = btn.getAttribute('data-filter');
    applyFilters();
  });
});

Для диапазонов цены удобнее заранее раскладывать товары по корзинам (data-price-range=“0-25000”, “25000-50000” и так далее), а не сравнивать числа в скрипте - так легче контролировать границы и не зависеть от того, как менеджер вбил цену в карточку. С чекбоксами вместо кнопок логика та же, только вместо одного значения в activeFilters храню массив выбранных категорий и проверяю через includes.

Фильтр и адресная строка: ссылки на готовые подборки

Без синхронизации с URL фильтр красиво работает, но ссылку на конкретную подборку не отправить - при обновлении страницы всё сбрасывается на «Все». Для рекламы и рассылок это неудобно: если ведёте трафик на «диваны до 30 000», логичнее слать на прямую ссылку с уже примененным фильтром, а не заставлять пользователя щёлкать кнопки заново.

function updateUrl() {
  var params = new URLSearchParams(window.location.search);
  params.set('category', activeFilters.category);
  history.replaceState(null, '', '?' + params.toString());
}

window.addEventListener('DOMContentLoaded', function () {
  var params = new URLSearchParams(window.location.search);
  var initial = params.get('category');
  if (initial) {
    activeFilters.category = initial;
    applyFilters();
  }
});

history.replaceState не создаёт новую запись в истории браузера при каждом клике, поэтому кнопка «назад» не превращается в перебор всех фильтров подряд. Для сложных сценариев, где фильтр должен ещё и подтягивать данные из CRM или пересчитывать остатки на складе, такую интеграцию обычно делаю уже не на голом JS, а с бэкендом - это отдельная услуга по кастомной доработке Tilda, потому что там появляются запросы к API и обработка ошибок, которых в статье не покажешь в двух скриптах.

Частые проблемы при внедрении и как их обойти

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

Проблема Причина Решение
Фильтр не работает на мобильной версии Tilda рендерит отдельный DOM для мобильного макета, атрибуты не продублированы Проверять data-атрибуты в обеих версиях верстки перед публикацией
Карточки «моргают» при первой загрузке Скрипт выполняется до полной отрисовки Zero Block Вешать обработчик на событие готовности Tilda вместо обычного DOMContentLoaded
После фильтрации ломается сетка, остаются пустые места display: none оставляет карточку в потоке grid/flex с некорректным подсчётом колонок Использовать flex-wrap и не задавать карточкам жёсткую ширину в процентах от общего числа
Фильтр конфликтует с корзиной T‑Store Скрипты T‑Store переинициализируют DOM после добавления товара в корзину Инициализировать фильтр повторно на событие обновления каталога магазина

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

Когда стандартных блоков не хватает

Кастомный скрипт

от 3 000 ₽

Подробнее →

Частые вопросы

Можно ли сделать фильтр на Тильде без программиста?

Простую фильтрацию по одной категории через кнопки и data-атрибуты можно собрать самому, если разобраться с блоком T123 и базовым JavaScript - шаблон из статьи выше покрывает большинство случаев. Как только добавляются несколько параметров одновременно, диапазоны цен, синхронизация с URL или связка с CRM, время на самостоятельную отладку обычно превышает стоимость заказа готового решения.

Работает ли такой фильтр на мобильной версии сайта?

Да, но верстку и data-атрибуты нужно проверять отдельно для мобильной версии - Tilda хранит десктопный и мобильный макет как разные наборы блоков, и если атрибут добавлен только в десктопной версии, на телефоне фильтр просто не найдёт карточки для сравнения.

Как фильтр повлияет на SEO, если контент скрывается через display:none

Если весь текст карточек физически присутствует в HTML-коде страницы, а скрывается только визуально через CSS, поисковики его индексируют - Google и Яндекс давно умеют рендерить JavaScript и читать финальный DOM. Проблем не будет, если не прятать контент через display:none изначально в верстке блока, тогда его вообще не отдаёт сервер - фильтр должен скрывать уже отрисованные карточки, а не подгружать их динамически без серверного рендеринга.

Сколько стоит внедрение кастомного фильтра на Tilda?

Простая фильтрация по одному параметру с базовой разметкой обходится от 3 000 ₽ - это разовая доработка существующего блока. Комплексный фильтр с несколькими критериями, синхронизацией с URL и интеграцией с CRM или остатками склада - это уже полноценная кастомная доработка, от 40 000 ₽ в зависимости от количества параметров и источников данных.

Есть задача?

Обсудим в мессенджере

Расскажите, что нужно сделать — отвечу в течение 4 часов в рабочее время. Первая консультация бесплатно.

Самозанятый Калинкин Н. А. · работаю с физлицами и юрлицами

Продолжая пользование настоящим сайтом Вы выражаете своё согласие на обработку Ваших персональных данных (файлов куки) с использованием Yandex.Metrika.
Понятно