Разработка системы сравнения товаров
Сравнение товаров — функция, которая упрощает выбор в категориях с высокой когнитивной нагрузкой: электроника, бытовая техника, автозапчасти, строительные материалы. Пользователь добавляет несколько позиций и видит их характеристики в едином табличном виде. Реализация кажется простой, пока не сталкиваешься с разнородными атрибутами, вложенными категориями и требованием к производительности.
Архитектурные решения
Первый вопрос: где хранить список сравнения — на сервере или в браузере?
LocalStorage / SessionStorage: нет привязки к аккаунту, мгновенная запись, работает без авторизации. Ограничение — данные не переносятся между устройствами. Для большинства B2C-магазинов достаточно.
Серверная сессия (Redis): список привязан к сессии, сохраняется при переходах, синхронизируется между вкладками. Нужна авторизация или анонимный сессионный токен.
БД с привязкой к пользователю: постоянное хранение, история сравнений. Избыточно для большинства задач, оправдано в B2B-кабинетах.
Оптимальная схема: LocalStorage + merge с серверным списком при авторизации (аналогично корзине).
Модель данных атрибутов
Главная сложность — атрибуты разных товаров могут не совпадать. Телевизор имеет «диагональ», холодильник — «объём камеры», и оба могут попасть в одну таблицу сравнения, если пользователь случайно добавил разные категории.
Схема атрибутов:
attributes (
id, name, unit, type, -- тип: numeric | text | boolean | range
category_id, -- атрибут принадлежит категории
comparable, -- показывать ли в сравнении
highlight_if_best -- подсвечивать ли лучшее значение
)
product_attributes (
product_id, attribute_id, value_numeric, value_text, value_boolean
)
Флаг comparable позволяет исключить из таблицы сравнения атрибуты типа «артикул» или «страна производства», которые не дают информации для выбора.
Логика «лучшего значения»
Подсветка лучшего значения в колонке — важная UX-деталь: пользователь сразу видит, у какого товара больше ОЗУ или ниже энергопотребление. Требует знания семантики атрибута:
type AttributeComparison = {
direction: 'higher_is_better' | 'lower_is_better' | 'none';
};
function highlightBest(values: number[], direction: AttributeComparison['direction']): number {
if (direction === 'higher_is_better') return Math.max(...values);
if (direction === 'lower_is_better') return Math.min(...values);
return NaN; // не подсвечиваем
}
Это поле highlight_direction хранится в таблице attributes. Для атрибутов типа «цвет» или «материал» подсветка не применяется.
Отображение таблицы сравнения
Классическая структура: строки — атрибуты, колонки — товары. Но при 20+ атрибутах нужна группировка:
Общие характеристики
├── Бренд
├── Страна производства
└── Гарантия
Дисплей
├── Диагональ
├── Разрешение
└── Частота обновления
Производительность
├── Процессор
├── ОЗУ
└── Накопитель
Группы схлопываются/раскрываются. Дополнительный фильтр — «Показать только различия» — скрывает строки, где у всех товаров одинаковое значение. Это ключевая фича: в таблице из 50 строк часто 30 совпадают.
function filterDifferences(rows: ComparisonRow[]): ComparisonRow[] {
return rows.filter(row => {
const values = row.products.map(p => p.value);
return new Set(values).size > 1; // оставляем только строки с разными значениями
});
}
Sticky-шапка и горизонтальный scroll
При 4–5 товарах в сравнении таблица выходит за ширину экрана. Решение:
- Горизонтальный scroll контейнера, при этом левая колонка (названия атрибутов) — position: sticky; left: 0
- Шапка с фото и названиями товаров — position: sticky; top: 0 (или фиксируется при скролле вниз)
- На мобайле: горизонтальный свайп через
overflow-x: autoсscroll-snap-type: x mandatory
CSS-трюк для sticky-шапки с горизонтальным scrollом — оба sticky должны применяться к разным элементам. Шапка sticky в родительском скролл-контейнере, левая колонка sticky в том же контейнере.
Ограничение количества товаров в сравнении
Рекомендуемый лимит: 3–5 товаров. Больше — таблица нечитаема. При попытке добавить 6-й показываем уведомление: «Уберите один товар, чтобы добавить новый». UX-паттерн: вместо блокировки предлагаем заменить один из существующих.
Кнопка «Добавить в сравнение» на карточке товара меняет состояние: добавлено/удалено. Состояние синхронизировано глобально (Zustand, Redux Toolkit) — при добавлении на странице каталога кнопка на странице товара тоже показывает «добавлен».
Плавающая панель сравнения
Пока пользователь листает каталог и добавляет товары, внизу экрана (или сбоку) показывается фиксированная панель с текущим списком сравнения и кнопкой «Сравнить». Реализация: fixed-позиционированный компонент, появляется когда список непустой, анимация через CSS transform translateY.
Интеграция с каталогом
На странице категории рядом с каждым товаром — чекбокс или иконка «сравнить». При отмеченных 2+ товарах — появляется CTA-кнопка «Сравнить выбранное». Это конверсионный паттерн: пользователь не уходит на отдельную страницу до тех пор, пока не выбрал хотя бы двух кандидатов.
Sharing и deep links
URL страницы сравнения должен содержать список товаров: /compare?ids=42,117,203. Это позволяет:
- Поделиться ссылкой (в B2B — отправить коллеге)
- Вернуться к сравнению через историю браузера
- Индексировать популярные сравнения в поисковиках (с noindex на длинных хвостах)
Сроки
- Базовое сравнение (LocalStorage, статичная таблица, кнопка на карточке): 1–2 недели
- Полноценная система (группировка атрибутов, подсветка лучшего, sticky-шапка, плавающая панель, deep links): 2–4 недели
- Интеграция с существующим каталогом на нестандартной модели данных добавляет 1–2 недели
Система сравнения окупается в категориях, где средний чек высокий и пользователь тратит время на выбор — техника, инструмент, оборудование. В магазинах с простым ассортиментом приоритет ниже.







