Разработка фильтрации по диапазону цен 1С-Битрикс

Стандартный умный фильтр 1С-Битрикс не всегда удобен для покупателей: два текстовых поля вместо наглядного слайдера и перезагрузка страницы при каждом изменении. Мы разрабатываем кастомные решения для фильтрации по цене — с ползунками, мгновенным обновлением каталога и поддержкой разных типов цен. Наша команда реализует проект под ключ, обеспечивая стабильную работу и последующее сопровождение.
Услуги, которые мы предлагаем
Показано 1 из 1Все 1626 услуг
Разработка фильтрации по диапазону цен 1С-Битрикс
Средний
~1-2 недели
Часто задаваемые вопросы

Стандартный умный фильтр 1С-Битрикс включает ценовой диапазон в виде двух текстовых полей «от» и «до». В большинстве случаев этого достаточно — пока не появляется требование: слайдер с двумя ползунками, мгновенное обновление каталога без перезагрузки страницы, корректная работа с несколькими типами цен и граничными значениями из реальных данных. Мы реализовали десятки таких решений — от простого слайдера до логарифмической шкалы для каталогов с разбросом цен от $3–4.5k. Стандартный компонент фильтра не покрывает эти сценарии без кастомной разработки. Свяжитесь с нами, чтобы получить консультацию по вашему проекту.

Как работает ценовой фильтр в 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>';

Кейс: каталог электроники с широким ценовым диапазоном

У клиента — интернет-магазин бытовой техники — каталог имел диапазон цен от $3–4.5k. Стандартные поля ввода «от/до» работали некорректно: пользователи вводили значения вручную, ошибались с нулями. Мы предложили слайдер с логарифмической шкалой — нижний диапазон ($3–50.) занимает столько же экранного пространства, сколько верхний ($1k–4.5k.). В результате слайдер сократил время выбора диапазона в 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 проектов по кастомизации фильтров.

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

Наши компетенции:

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1542
  • Разработка веб-сайта для компании ФИКСПЕР
    Разработка веб-сайта для компании ФИКСПЕР
    1060
  • Разработка виджета онлайн-записи на прием для медицинского центра
    Разработка виджета онлайн-записи на прием для медицинского центра
    835
  • Разработка на базе 1С Предприятие для компании МИРСАНБЕЛ
    Разработка на базе 1С Предприятие для компании МИРСАНБЕЛ
    928
  • Разработка сайта на CRM Битрикс24 для компании DOLBIMBY
    Разработка сайта на CRM Битрикс24 для компании DOLBIMBY
    864
  • Разработка на базе Битрикс24 для компании ТЕХНОТОРГКОМПЛЕКС
    Разработка на базе Битрикс24 для компании ТЕХНОТОРГКОМПЛЕКС
    1222