Оформление
Примеры ранжирования
Во всех примерах RANKING_ID — идентификатор ранжирования из редактора в кабинете. Скрипт Sales Ninja должен быть установлен на странице.
Каталог: переставить карточки на странице
Карточки уже нарисованы сайтом, у каждой есть атрибут data-product-id с id предложения из фида. Скрипт сам прочитает этот атрибут, поэтому getId не нужен.
html
<div id="catalog">
<article data-product-id="sku-101">…</article>
<article data-product-id="sku-102">…</article>
<article data-product-id="sku-103">…</article>
</div>js
const catalog = document.querySelector('#catalog')
const cards = [...catalog.querySelectorAll('[data-product-id]')]
const { status, items } = await ninja('rankProducts', {
rankingId: 'RANKING_ID',
items: cards,
})
if (status === 'ranked') {
catalog.append(...items.map(({ item }) => item))
}append переносит существующие элементы, а не копирует их: обработчики событий и состояние карточек сохраняются.
Группы на странице
Товары разбиты на блоки, например по мощности двигателя. Порядок нужен внутри каждого блока, а сами блоки остаются на своих местах.
js
const groups = [...document.querySelectorAll('.power-group')]
const cardsByGroup = groups.map((group) => [...group.querySelectorAll('[data-product-id]')])
const { items } = await ninja('rankProducts', {
rankingId: 'RANKING_ID',
items: cardsByGroup,
})
items.forEach((rankedGroup, groupIndex) => {
groups[groupIndex].append(...rankedGroup.map(({ item }) => item))
})Ответ повторяет форму запроса: на массив групп приходит массив групп в том же порядке.
Данные до отрисовки
Сайт получает товары из своего API и рисует их сам. Тогда удобнее упорядочить данные до отрисовки и указать, где лежит идентификатор товара.
js
const products = await fetch('/api/catalog?section=boots').then((response) => response.json())
const ranked = await Promise.race([
ninja('rankProducts', {
rankingId: 'RANKING_ID',
items: products,
getId: (product) => product.offerId,
timeout: 300,
}),
// страховка: если скрипт Sales Ninja не загрузился, страница не ждёт дольше 300 мс
new Promise((resolve) => setTimeout(() => resolve(null), 300)),
])
renderCatalog(ranked ? ranked.items.map(({ item }) => item) : products)timeout задаёт, сколько миллисекунд с момента вызова страница готова ждать ответа. Если ответ не успел, rankProducts возвращает исходный список без изменений со статусом timeout, поздний ответ не применяется, и страница рисуется без задержки.