События виджета
У виджета два канала общения с сайтом:
- Единая шина
SearchForceWidget.push(['имя', данные])/.on(имя, обработчик)— через неё уходит аналитика (корзина, покупка, клики) и можно обмениваться любыми своими сообщениями между карточкой, скриптами тенанта и сайтом. - DOM-события (
CustomEvent) — узкие хуки для управления переходом по поиску, когда вы хотите маршрутизировать его сами (SPA, нестандартный роутинг).
Единая шина: push() / on()
js
// сайт СООБЩАЕТ виджету о действии (аналитика уходит сама)
window.SearchForceWidget = window.SearchForceWidget || [];
SearchForceWidget.push(['addToCart', { objectId: 12345, amount: '2599.00' }]);
SearchForceWidget.push(['purchase', { orderId: 'A-10042', amount: '2599.00' }]);
// сайт СЛУШАЕТ события виджета (нужен уже загруженный скрипт)
const off = SearchForceWidget.on('addToCart', (payload) => { /* … */ });
off(); // отпискаpush(...) — единственный вход для отправки. Он же очередь готовности: до загрузки скрипта виджета события копятся в массиве и уходят, как только он появится, — поэтому вызывать можно в любой момент и не важно, как подключён виджет (обычный тег, async, вставка через GTM). Известные события встроенный подписчик доставляет в аналитику сам:
| Событие | payload | В аналитику |
|---|---|---|
productClick | { hit } (из поиска) или { objectId } | клик, с атрибуцией к поиску если есть |
addToCart | { objectId, amount? } или { hit } | добавление в корзину |
purchase | { orderId, amount?, objectIds? } | покупка (идемпотентна по orderId) |
Подписка on(...) требует уже загруженного скрипта виджета: обработчики в очередь не кладутся — вешайте их из своего кода после подключения виджета или внутри SearchForceWidget.ready(...).
Что виджет эмитит сам
Эти события шина отдаёт вам — подписывайтесь, чтобы ставить цели в своей аналитике (Яндекс.Метрика, GA4, VK) или связывать поиск с логикой сайта. В нашу аналитику они НЕ уходят повторно: поиски мы пишем на сервере из самого запроса, клик уже посчитан событием productClick.
| Событие | payload | Когда |
|---|---|---|
open | { surface: 'autocomplete' | 'fullpage' } | покупатель открыл поиск: развернул оверлей либо на странице появилась полная выдача |
search | { query, nbHits, queryId? } | получена выдача — на каждый запрос, включая ввод по буквам |
searchSettled | { query, nbHits, queryId? } | покупатель перестал печатать (пауза 1,5 с) — один раз на серию набора |
noResults | { query } | серия набора закончилась пустой выдачей |
resultClick | { objectId, queryId?, position? } | клик именно по результату поиска |
diagnostic | { scope, reason, message } | виджет отверг ваш вызов |
Для целей во внешней аналитике берите searchSettled, а не search: живой поиск отправляет запрос на каждый ввод, и одно слово даст десяток срабатываний. Запросы короче двух символов и служебные (*) не эмитятся вовсе — тем же порогом мы считаем поиски на сервере.
Для Яндекс.Метрики руками писать ничего не нужно
Готовый набор целей и виртуальные хиты поиска включаются в кабинете одной галочкой — см. Яндекс.Метрика. Слушайте события сами, только если нужна своя логика или другая система аналитики.
Пример своей цели: «искал» в GA4 один раз за визит.
js
SearchForceWidget.on('searchSettled', ({ query, nbHits }) => {
if (typeof gtag !== 'function' || sessionStorage.getItem('sf_search_goal')) return;
sessionStorage.setItem('sf_search_goal', '1');
gtag('event', 'view_search_results', { search_term: query, results_count: nbHits });
});Помните, что query — текст, который набрал покупатель: в поиск иногда вводят телефоны и адреса, поэтому решайте осознанно, отправлять ли его в чужую систему.
Обратно виджет эмитит diagnostic — когда отверг ваш вызов (неверный формат поля). Подпишитесь, если хотите видеть такие отказы в своём мониторинге, а не только в консоли браузера:
js
SearchForceWidget.on('diagnostic', ({ scope, reason, message }) => {
// message — тот же текст, что виджет печатает в console.warn
myMonitoring.warn(message, { event: scope, reason });
});objectId/orderId — строка или число. amount — сумма в основных единицах валюты проекта (рубли, не копейки): '2599.00' или 2599; необязательна, но без неё не наберутся отчёты выручки. Правила разбора и что происходит при неверном формате — в «Трекинг → Корзина и покупки».
Полные примеры для корзины и чекаута — в «Трекинг → Корзина и покупки». Неизвестные имена событий аналитика игнорирует — шину можно использовать под свои сообщения.
Клики по товарам виджет репортит сам
Клик по карточке в выдаче и подсказках уходит в аналитику автоматически (с атрибуцией queryID/позиции) — пушить productClick вручную нужно только для кликов вне поиска.
DOM-события: управление переходом по поиску
Летят на элементе-триггере из autocomplete.init({ input }), всплывают (bubbles: true) — слушать можно и на нём, и на document:
js
document.addEventListener('sf:search-submit', function (e) {
console.log('Поисковый запрос:', e.detail.query);
});sf:search-submit
Срабатывает при отправке запроса (Enter, «Показать все результаты», выбор из истории/популярных/категорий) — только если searchUrl не задан. С заданным searchUrl виджет вместо события переходит на страницу выдачи.
Поле detail | Когда | Значение |
|---|---|---|
query | обычный текстовый запрос | Строка запроса. |
query + refinements | выбор категории из подсказок | query: '' и refinements: { <поле-фасет>: [значение] }. |
js
document.addEventListener('sf:search-submit', function (e) {
var params = new URLSearchParams();
if (e.detail.query) {
params.set('q', e.detail.query);
}
myRouter.push('/search/?' + params.toString()); // ваш роутер
});Скрипты тенанта
Логику связи виджета с сайтом можно вынести в скрипты тенанта (scripts.ts), которые редактируются в редакторе виджета и запекаются в бандл. Они получают ту же шину и выполняются один раз при загрузке:
ts
import { defineTenantScripts } from '@searchforce/runtime'
export default defineTenantScripts(({ events }) => {
events.on('addToCart', (payload) => {
// вызвать API корзины сайта
});
// Цель «искал» в Метрике клиента — один раз за визит.
events.on('searchSettled', () => {
// тот же код, что выше; в скриптах тенанта подписка доступна сразу,
// ждать загрузки бандла не нужно — они его часть.
});
});Полностраничная выдача синхронизирует запрос и фильтры с URL сама (параметр enableRouting, см. справочник виджетов).