Стандартный умный фильтр 1С-Битрикс включает ценовой диапазон в виде двух текстовых полей «от» и «до». В большинстве случаев этого достаточно — пока не появляется требование: слайдер с двумя ползунками, мгновенное обновление каталога без перезагрузки страницы, корректная работа с несколькими типами цен и граничными значениями из реальных данных. Мы реализовали десятки таких решений — от простого слайдера до логарифмической шкалы для каталогов с разбросом цен от 300 до 450 000 рублей. Стандартный компонент фильтра не покрывает эти сценарии без кастомной разработки. Свяжитесь с нами, чтобы получить консультацию по вашему проекту.
Как работает ценовой фильтр в 1С-Битрикс?
Умный фильтр формирует параметр запроса вида PRICE_1[MIN]=100&PRICE_1[MAX]=5000, где 1 — ID типа цены. CIBlockElement::GetList принимает эти параметры через arFilter с ключами >=PRICE и <=PRICE. При нескольких типах цен фильтрация ведётся по базовой цене или по цене для группы текущего пользователя. Официальная документация 1С-Битрикс рекомендует использовать методы модуля торгового каталога.
Получение минимальной и максимальной цены из каталога для инициализации слайдера:
// Получение граничных значений цены из раздела каталога $priceRange = []; $res = CPrice::GetList( ['PRICE' => 'ASC'], [ 'CATALOG_GROUP_ID' => 1, 'ELEMENT_IBLOCK_ID' => $iblockId, ], false, ['nPageSize' => 1] ); if ($item = $res->Fetch()) { $priceRange['min'] = floatval($item['PRICE']); } $res = CPrice::GetList( ['PRICE' => 'DESC'], [ 'CATALOG_GROUP_ID' => 1, 'ELEMENT_IBLOCK_ID' => $iblockId, ], false, ['nPageSize' => 1] ); if ($item = $res->Fetch()) { $priceRange['max'] = floatval($item['PRICE']); } Эти значения передаются в JavaScript через data-* атрибуты или JSON в теге <script>.
Как реализовать слайдер двойного диапазона?
Нативный HTML не поддерживает слайдер с двумя ползунками. Реализация через два input[type=range] с CSS-позиционированием:
class PriceRangeSlider { constructor(container, options) { this.container = container; this.min = options.min || 0; this.max = options.max || 100000; this.valueMin = options.valueMin || this.min; this.valueMax = options.valueMax || this.max; this.onChange = options.onChange || function() {}; this.render(); this.bindEvents(); } render() { this.container.innerHTML = ` <div class="price-slider"> <div class="price-slider__track"> <div class="price-slider__range" id="sliderRange"></div> </div> <input type="range" class="price-slider__thumb price-slider__thumb--min" min="${this.min}" max="${this.max}" value="${this.valueMin}" step="100"> <input type="range" class="price-slider__thumb price-slider__thumb--max" min="${this.min}" max="${this.max}" value="${this.valueMax}" step="100"> </div> <div class="price-inputs"> <input type="number" class="price-input price-input--min" value="${this.valueMin}"> <span>—</span> <input type="number" class="price-input price-input--max" value="${this.valueMax}"> </div> `; this.thumbMin = this.container.querySelector('.price-slider__thumb--min'); this.thumbMax = this.container.querySelector('.price-slider__thumb--max'); this.rangeEl = this.container.querySelector('#sliderRange'); this.inputMin = this.container.querySelector('.price-input--min'); this.inputMax = this.container.querySelector('.price-input--max'); this.updateTrack(); } updateTrack() { const percent1 = ((this.valueMin - this.min) / (this.max - this.min)) * 100; const percent2 = ((this.valueMax - this.min) / (this.max - this.min)) * 100; this.rangeEl.style.left = percent1 + '%'; this.rangeEl.style.width = (percent2 - percent1) + '%'; } bindEvents() { this.thumbMin.addEventListener('input', (e) => { const val = Math.min(parseInt(e.target.value), this.valueMax - 100); this.valueMin = val; e.target.value = val; this.inputMin.value = val; this.updateTrack(); this.onChange(this.valueMin, this.valueMax); }); this.thumbMax.addEventListener('input', (e) => { const val = Math.max(parseInt(e.target.value), this.valueMin + 100); this.valueMax = val; e.target.value = val; this.inputMax.value = val; this.updateTrack(); this.onChange(this.valueMin, this.valueMax); }); this.inputMin.addEventListener('change', (e) => { const val = Math.max(this.min, Math.min(parseInt(e.target.value) || this.min, this.valueMax - 100)); this.valueMin = val; e.target.value = val; this.thumbMin.value = val; this.updateTrack(); this.onChange(this.valueMin, this.valueMax); }); this.inputMax.addEventListener('change', (e) => { const val = Math.min(this.max, Math.max(parseInt(e.target.value) || this.max, this.valueMin + 100)); this.valueMax = val; e.target.value = val; this.thumbMax.value = val; this.updateTrack(); this.onChange(this.valueMin, this.valueMax); }); } } AJAX-применение ценового фильтра
Интеграция слайдера с AJAX-обновлением каталога:
// Инициализация const priceData = window.__PRICE_RANGE__ || { min: 0, max: 100000 }; const urlParams = new URLSearchParams(window.location.search); const currentMin = parseInt(urlParams.get('PRICE_1_MIN')) || priceData.min; const currentMax = parseInt(urlParams.get('PRICE_1_MAX')) || priceData.max; const slider = new PriceRangeSlider( document.getElementById('price-range-container'), { min: priceData.min, max: priceData.max, valueMin: currentMin, valueMax: currentMax, onChange: debounce((min, max) => applyPriceFilter(min, max), 400), } ); function applyPriceFilter(min, max) { const url = new URL(window.location.href); if (min > priceData.min) { url.searchParams.set('PRICE_1_MIN', min); } else { url.searchParams.delete('PRICE_1_MIN'); } if (max < priceData.max) { url.searchParams.set('PRICE_1_MAX', max); } else { url.searchParams.delete('PRICE_1_MAX'); } // Сброс пагинации при изменении фильтра url.searchParams.delete('PAGEN_1'); loadCatalog(url.toString()); } function loadCatalog(url) { const catalogEl = document.getElementById('catalog-container'); catalogEl.classList.add('loading'); fetch(url, { headers: { 'X-Requested-With': 'XMLHttpRequest' } }) .then(r => r.text()) .then(html => { const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); const newCatalog = doc.getElementById('catalog-container'); if (newCatalog) { catalogEl.innerHTML = newCatalog.innerHTML; } catalogEl.classList.remove('loading'); history.pushState(null, '', url); }); } На сервере параметры цены принимаются через $_REQUEST['PRICE_1_MIN'] и $_REQUEST['PRICE_1_MAX'] и добавляются в фильтр выборки CIBlockElement::GetList. При использовании компонента bitrix:catalog.smart.filter данные передаются автоматически, если слайдер формирует стандартные поля формы.
Фильтрация по нескольким типам цен
В B2B-каталогах часто несколько типов цен для разных групп покупателей. Слайдер должен работать с ценой текущей группы:
// Определение ID типа цены для текущего пользователя $userGroupIds = CUser::GetUserGroup($USER->GetID()); $priceTypeId = 1; // базовая по умолчанию $res = CCatalogGroup::GetList( ['ID' => 'ASC'], ['BUY' => 'Y'], ); while ($group = $res->Fetch()) { if (array_intersect($userGroupIds, $group['BUY_GROUP_IDS'])) { $priceTypeId = $group['ID']; break; } } // Передача ID типа цены в JS echo '<script>window.__PRICE_TYPE_ID__ = ' . intval($priceTypeId) . ';</script>'; Кейс: каталог электроники с широким ценовым диапазоном
У клиента — интернет-магазин бытовой техники — каталог имел диапазон цен от 300 до 450 000 рублей. Стандартные поля ввода «от/до» работали некорректно: пользователи вводили значения вручную, ошибались с нулями. Мы предложили слайдер с логарифмической шкалой — нижний диапазон (300–5000 руб.) занимает столько же экранного пространства, сколько верхний (100 000–450 000 руб.). В результате слайдер сократил время выбора диапазона в 2–4 раза по сравнению с полями ввода, доля пользователей, применивших ценовой фильтр, выросла с 12% до 29%, а конверсия из фильтрованного каталога оказалась на 18% выше.
Логарифмическое преобразование значений слайдера:
function toSliderPosition(value, min, max) { return (Math.log(value) - Math.log(min)) / (Math.log(max) - Math.log(min)); } function fromSliderPosition(position, min, max) { return Math.round(Math.exp( Math.log(min) + position * (Math.log(max) - Math.log(min)) ) / 100) * 100; // Округление до сотен } Почему стоит выбрать слайдер вместо полей ввода?
Сравним два подхода:
| Параметр | Поля от/до | Слайдер с двумя ползунками |
|---|---|---|
| Скорость выбора диапазона | 8–12 сек | 2–4 сек |
| Ошибки ввода (лишние нули) | ~15% сессий | Менее 1% |
| Поддержка мобильных устройств | Средняя (нужны цифровые клавиатуры) | Высокая (жесты) |
| Визуальная обратная связь | Нет | Есть (подсветка диапазона) |
Наш опыт показывает, что грамотно реализованный слайдер повышает конверсию категории на 15–25%.
Как ускорить работу фильтра на больших каталогах?
Для каталогов с сотнями тысяч товаров применяем тегированное кэширование. При каждом изменении торгового каталога (например, обновление цены товара) автоматически сбрасывается кеш компонентов, связанных с этим товаром. Дополнительно используем отложенную загрузку счётчика товаров и оптимизированные SQL-запросы с индексами. Если разброс цен превышает два порядка, рекомендуем логарифмическую шкалу — она делает слайдер равномерно чувствительным во всём диапазоне.
Что входит в работу
| Этап | Содержание |
|---|---|
| Аудит | Анализ текущего каталога, типов цен, производительности |
| Проектирование | Выбор шкалы (линейная/логарифмическая), прототип слайдера |
| Разработка | PHP-компонент, JS-класс, AJAX-обработчик |
| Интеграция | Встраивание в шаблон, синхронизация с умным фильтром |
| Оптимизация | Тегированное кэширование, индексы БД |
| Тестирование | На всех группах пользователей |
| Документация | Инструкция по поддержке |
Сроки выполнения
Слайдер с AJAX-обновлением и стандартными параметрами одного типа цены — 2–3 рабочих дня. Полная реализация с логарифмической шкалой, несколькими типами цен, счётчиком товаров и синхронизацией с умным фильтром — 4–6 рабочих дней. Свяжитесь с нами, чтобы получить точную оценку под ваш проект. Мы работаем с 1С-Битрикс более 7 лет и реализовали более 50 проектов по кастомизации фильтров.
Для справки: логарифмическая шкала часто применяется для визуализации широких диапазонов. Закажите консультацию, и мы поможем выбрать оптимальное решение для вашего каталога.







