JavaScript API и события
Обновлено
Свои сценарии Slate подключаются поверх готовой темы. Общее пространство имён — window.waTheme, собственные методы Slate находятся в waTheme.slate. Тема работает классическими скриптами без JavaScript-фреймворка; собственный код должен учитывать Safari 15, Chrome 90 и Firefox 90. jQuery в коде темы не используется, а на странице может отсутствовать: его подключение зависит от настройки «Прочее → Скорость загрузки → Библиотека jQuery». Не стройте свой код на $() — используйте API ниже и обычные возможности браузера.
Подключение собственного сценария
Поместите файл в custom/js/ темы «Сайта» и подключите его из custom/scripts.html:
{call assets file="custom/js/project.js"}В установленной копии темы объявите файл в theme.xml как пользовательский: <file path="custom/js/project.js" custom="1"></file>. custom/scripts.html подключается в конце <body> после сценариев темы. Макрос assets добавляет версию файла к адресу. Поставляемые theme.js и theme.min.js редактировать для этого не нужно.
(function () {
'use strict';
waTheme.slate.ready(function () {
var button = document.querySelector('[data-project-open]');
if (!button) { return; }
button.addEventListener('click', function () {
waTheme.slate.ui.open('project-panel');
});
});
})();Пример предполагает панель с data-slate-drawer="project-panel". Для обычной кнопки открытия достаточно штатного data-slate-toggle="project-panel", без собственного обработчика.
Общее API
| Метод | Назначение |
|---|---|
waTheme.slate.ready(fn) |
Выполняет функцию после готовности DOM или сразу, если DOM уже готов |
.on(name, handler) |
Подписывает обработчик на событие документа |
.off(name, handler) |
Убирает тот же обработчик |
.emit(name, detail) |
Отправляет CustomEvent, данные доступны в event.detail |
.token(name) |
Возвращает вычисленное строковое значение CSS-переменной с #slate |
.log(...) |
Пишет в консоль при включённом режиме диагностики |
.postBody(src) |
Готовит тело POST-запроса из формы, FormData или URLSearchParams: application/x-www-form-urlencoded, а multipart/form-data — только если выбран файл |
.listMore(opts) |
Подключает к своему списку кнопку «Показать ещё» или подгрузку при прокрутке |
waTheme.slate.on('slate:catalog-updated', function (event) {
var url = event.detail.url;
// Обновить собственный элемент после загрузки каталога.
});ready сообщает о готовности документа, а не об окончании загрузки всех изображений. При замене содержимого каталога собственные элементы нужно обрабатывать отдельно по событию обновления.
Свои POST-запросы
Часть хостингов отвечает ошибкой 403 на любой запрос с multipart/form-data. Поэтому корзина, отзывы, подписка и панель темы отправляют данные через postBody, и свой fetch стоит строить так же:
fetch(form.action, {
method: 'POST',
body: waTheme.slate.postBody(form),
credentials: 'same-origin'
});Обычная отправка формы с enctype="multipart/form-data" без выбранного файла тема сама переключает на urlencoded.
Своя подгрузка списка
listMore даёт своему списку то же листание, что у каталога, блога, «Фото» и сообщества: кнопку с блокировкой на время запроса, подгрузку при прокрутке, обновление адреса страницы. Режим задаёт атрибут блока страниц: data-slate-more="button" или "scroll", а data-page и data-pages — текущую страницу и их число; при другом значении скрипт ничего не делает. Чтобы список следовал настройке «Общие настройки → Основные настройки → Как листать списки», печатайте в атрибут её значение. Функция next загружает следующую порцию и возвращает добавленные элементы:
waTheme.slate.ready(function () {
var paging = document.querySelector('[data-my-paging]');
var list = document.querySelector('[data-my-list]');
waTheme.slate.listMore({
paging: paging, list: list, history: true,
next: function (state) {
state.nextUrl = '?page=' + (state.page + 1);
return fetch(state.nextUrl).then(function (r) { return r.text(); }).then(function (html) {
var doc = new DOMParser().parseFromString(html, 'text/html');
var added = [];
doc.querySelectorAll('[data-my-list] > li').forEach(function (li) {
added.push(list.appendChild(document.importNode(li, true)));
});
return { items: added, url: state.nextUrl };
});
}
});
});При ошибке загрузки посетитель переходит по state.nextUrl обычным переходом.
Панели, карусели и уведомления
waTheme.slate.ui.open(id), .close(id) и .toggle(id) управляют панелью по значению data-slate-drawer. Для динамически добавленной области есть .initDrawers(root). Выпадающие меню закрываются через .closeDropdowns(), раскрываемые секции управляются .collapse(id, open), вкладки — .selectTab(id). .fitPopup(menu, anchor, side) подгоняет собственную всплывающую панель под видимую область экрана с отступом 16 px: ограничивает ширину, передаёт доступную высоту в CSS-переменной --slate-popup-height (используйте её в max-height своей панели) и при нехватке места открывает панель с другой стороны якоря; side — left или right для боковой панели, без него панель считается выпадающей. Штатные выпадающие меню и компактные фильтры делают это сами. Предпочитайте штатные атрибуты поведения собственным обработчикам.
API карусели принимает её корневой DOM-элемент: waTheme.slate.carousel.go(el, index), .next(el), .prev(el), .index(el), .init(el). Индекс начинается с нуля; init нужен для карусели, добавленной после загрузки страницы. Событие slate:slide передаёт {id, index}.
Стрелки карусели можно вынести за её корень, например в заголовок блока. В значении data-slate-carousel-prev и data-slate-carousel-next укажите CSS-селектор корня [data-slate-carousel]:
<button type="button" data-slate-carousel-prev="#project-gallery" aria-label="Назад">←</button>
<button type="button" data-slate-carousel-next="#project-gallery" aria-label="Вперёд">→</button>
<div id="project-gallery" data-slate-carousel="project-gallery" aria-label="Фотографии проекта">
<div data-slate-carousel-track>
<div data-slate-carousel-slide>Первый кадр</div>
<div data-slate-carousel-slide>Второй кадр</div>
</div>
</div>Селектор должен однозначно указывать на нужную карусель; некорректный селектор или отсутствующая цель игнорируются. Стрелки должны быть в DOM до инициализации карусели. Пустое значение атрибута сохраняет обычное поведение внутренней стрелки: она управляет ближайшей каруселью. Несколько кнопок могут управлять одной лентой; без кольцевого листания они получают disabled на соответствующем краю и скрываются, если все слайды помещаются в кадр. Изначально скрытые кнопки остаются скрытыми.
Цвет текста шапки по активному слайду
Когда шапка расположена поверх первого широкого слайдера blocks/slider с вариантом 04-wide, её текст может следовать активному слайду. У слайда задайте text_color: "light" для светлого текста или "dark" для тёмного. Пустое значение или "auto" возвращает исходные цвета шапки.
Разметка слайда передаёт это значение через data-slate-header-text. При загрузке и на событии slate:slide тема получает индекс через waTheme.slate.carousel.index(el) и переносит атрибут активного слайда на [data-slate="header"]. Цвет меняется только у частей настольной шапки без явно заданного фона. На телефоне, при обычном расположении шапки и у других слайдеров её цвета сохраняются.
Уведомления
var notice = window.SlateToast.show({
title: 'Готово',
text: 'Изменения применены',
duration: 4000
});
// При необходимости: notice.close();Текст уведомления печатается как текст, без HTML. Дополнительные параметры: image, link, linkText, kind, position. duration: 0 отключает автоматическое закрытие. waTheme.slate.toast — другое имя того же объекта.
Чтобы уведомление не повторялось, передайте cookie:
window.SlateToast.show({
text: 'Теперь можно записаться на консультацию онлайн',
cookie: { name: 'consultation-notice', value: 'v1', trigger: 'close', lifetime: 7 }
});name и value — непустые строки. При совпадении сохранённой cookie с value уведомление не создаётся; возвращаемый close() при этом безопасен. Измените value, чтобы показать новую редакцию сообщения.
trigger: "open"записывает cookie при показе.trigger: "close"(по умолчанию) записывает её только при нажатии крестика уведомления. Автозакрытие, вызовclose()и вытеснение новым уведомлением cookie не записывают.lifetime— срок в днях, по умолчанию 1. Допустимы положительные дробные числа; ноль, отрицательное или некорректное значение заменяется одним днём.
Без name или value правило отключено. Cookie записывается для пути / с SameSite=Lax; имя и значение кодируются.
Окна и панели
Для создания окна или выезжающей панели из своей разметки есть waTheme.slate.ui.show(options). Метод возвращает DOM-элемент панели или null, если открыть её нельзя:
waTheme.slate.ui.show({
id: 'project-info',
side: 'center',
className: 's-project-info',
html: '<h2>Информация</h2>' +
'<button type="button" data-slate-close="project-info">Закрыть</button>'
});id обязателен. html нужен для создания отсутствующей панели; существующая панель переиспользуется. side принимает left, right, top, bottom, center; center — модальное окно. open: false создаёт панель без открытия, delay задаёт задержку открытия в секундах. В html передавайте свою доверенную разметку, а не неэкранированный пользовательский ввод. Закрывайте панель через .close(id) или штатную кнопку, чтобы восстановились фокус и прокрутка.
Вкладки и плавная прокрутка
Вкладки используют корень data-slate-tabs, кнопки с role="tab" и data-slate-tab="id", панели с role="tabpanel" и соответствующим id. Вызов waTheme.slate.ui.selectTab(id) выбирает вкладку программно. При открытии адреса с #id и при событии hashchange тема активирует соответствующую вкладку. Нажатие её кнопки меняет фрагмент адреса через replaceState, без добавления записи в историю. Вид вкладок задаётся общей настройкой «Общие настройки → Карусели и вкладки → Как выглядят вкладки».
Обычная ссылка href="#booking" плавно прокручивает к элементу с id="booking" на этой же странице, обновляет адрес и переводит фокус на цель. Прокрутка учитывает scroll-margin-top; у стандартных секций он уже включает высоту закреплённой шапки. Для своего сценария есть API:
waTheme.slate.scrollTo('#booking', { focus: true }).then(function (result) {
// 'done' — завершено, 'cancelled' — прервано, 'missing' — цели нет.
});Цель — DOM-элемент, строка id или #id, либо координата страницы в пикселях. Необязательные offset и duration задают отступ сверху в пикселях и длительность в миллисекундах; focus по умолчанию выключен. Колесо, касание или клавиши прокрутки прерывают движение, новый вызов заменяет предыдущий. При системной настройке уменьшения движения переход происходит сразу. Чтобы отключить перехват якорных ссылок в своём блоке, добавьте ссылке или её предку data-slate-scroll="off". Ссылки вкладок обрабатываются отдельно.
Окно просмотра фотографий
Чтобы своя галерея открывала большие изображения в окне поверх страницы, достаточно разметки, без JavaScript:
<a href="/wa-data/public/site/img/hall-1400.webp"
data-slate-lightbox="hall" data-caption="Зал на первом этаже">
<img src="/wa-data/public/site/img/hall-480.webp" alt="Зал на первом этаже"
width="480" height="360" loading="lazy">
</a>href — адрес большого изображения; без JavaScript ссылка просто открывает его. Значение data-slate-lightbox объединяет ссылки в группу: окно листает все ссылки страницы с тем же значением, включая догруженные позже, кнопками, клавишами ←/→ и свайпом, по кругу. data-caption выводится подписью и служит alt большого изображения. Ссылки с нажатым Ctrl, Cmd, Shift или Alt открываются обычным образом. Группы photos и photos-block заняты плитками приложения «Фото» и блоком «Фото» главной, а productReview<номер отзыва>Images — снимками отдельного отзыва о товаре. Для своей галереи выберите другое имя.
Необязательный data-thumb задаёт миниатюру в нижней ленте; иначе используется картинка внутри ссылки. data-type="video" открывает href во встраиваемом проигрывателе: сервис должен разрешать встраивание. Видео не увеличивается и останавливается при смене кадра или закрытии.
Фотографии можно увеличивать кнопкой, двойным нажатием, колесом мыши или щипком. Двойное нажатие увеличивает кадр к выбранной точке; увеличенное изображение перемещается мышью или пальцем. Пока кадр увеличен, свайп двигает его, а не переключает снимок. Смена кадра сбрасывает увеличение. В окне есть счётчик и лента миниатюр; у единственного кадра стрелки и лента скрыты.
Для программного открытия используйте:
var box = waTheme.slate.lightbox.open([
{src: '/wa-data/public/site/img/hall-1400.webp',
thumb: '/wa-data/public/site/img/hall-480.webp', caption: 'Зал'}
], 0);open(items, index) принимает массив кадров и индекс с нуля, возвращает элемент окна или null. Обязательное поле кадра — src, необязательные — srcset, caption, thumb, type (image или video). Для увеличения из srcset выбирается самый крупный файл. Галерея товара использует этот же просмотр и после закрытия остаётся на последнем выбранном кадре.
Окно — панель с идентификатором slate-lightbox: оно отправляет slate:open и slate:close с этим id, закрывается через waTheme.slate.ui.close('slate-lightbox'), крестиком, Escape или щелчком мимо кадра и возвращает фокус на ссылку. Окно работает при любом режиме подключения jQuery.
Без собственного API работают модули «Фото» без jQuery (догрузка списка и переходы между кадрами на странице фотографии) и сворачивание строк плагина «Группы товаров»: их поведение задаётся разметкой приложения и плагина, событий они не отправляют.
API магазина
Методы магазина доступны при подключении соответствующих модулей. Для собственных блоков на страницах другого приложения сначала проверьте наличие объекта.
window.SlateCart.add(formOrFormData, {silent}) // Promise<data>; также принимает объект полей
window.SlateCart.save(itemId, quantity) // Promise<data>
window.SlateCart.remove(itemId, {silent}) // Promise<data>
window.SlateCart.clear({silent}) // Promise<data>: удалить все позиции
window.SlateCart.sync() // Promise<Array>: перечитать корзину с сервера
window.SlateCart.items() // Promise<Array> позиций корзины
window.SlateCart.bySku(skuId) // Promise<позиция|null>Позиция содержит id, product_id, sku_id, quantity и services — список {service_id, variant_id}; bySku возвращает первую совпавшую позицию. silent: true отключает уведомление и цель Метрики, событие cart:changed всё равно отправляется. sync() нужен, когда корзину могли изменить вне темы. Ошибки запросов обрабатывайте в .catch(). Количество в ответе — то, что принял сервер: при упоре в остаток оно может быть меньше запрошенного. Алиас waTheme.slate.cart содержит эти же методы и setCounters({count, total}); последний меняет счётчики интерфейса, без сохранения корзины на сервере. Счётчик корзины в шапке показывает число позиций (строк корзины), а не сумму штук.
У waTheme.slate.wishlist и waTheme.slate.compare одинаковые сигнатуры:
api.has(id) // boolean
api.ids() // массив числовых ID товаров
api.count() // число товаров
api.toggle(id, button) // boolean: включён ли товар после переключения
api.clear() // очистка спискаЗдесь api — один из двух объектов, а button — DOM-элемент кнопки для контекста уведомления. Эти списки хранятся в cookie. При включённой интеграции «Избранное PRO» избранным управляет плагин: штатные методы Slate становятся пустыми заглушками, не управляйте плагином через них.
Товар использует window.SlateProduct, также доступный как waTheme.productAPI:
window.SlateProduct.applySku(sku)
window.SlateProduct.refreshQuantityField(sku)
window.SlateProduct.refreshCartState()
window.SlateProduct.getQuantity()sku — запись артикула из данных товара. applySku применяет артикул, refreshQuantityField пересчитывает ограничения и значение количества, refreshCartState синхронизирует элементы количества с корзиной. getQuantity возвращает текущее количество. При открытом быстром просмотре операции работают в его контексте, иначе — на странице товара.
Поиск предоставляет waTheme.slate.search.open(), .close(), .recent(), .clearRecent(): открытие первой и закрытие всех панелей подсказок, чтение и очистка недавних запросов. Для каталога есть waTheme.slate.shop.loadCategoryPage(url, push, source): push определяет обновление истории, source — filters, sorting, views или null. Метод запускает загрузку списка; окончание отслеживайте событием.
События и их данные
Подписывайтесь через waTheme.slate.on(name, handler); данные получайте из handler как event.detail. События product:* всплывают от формы товара до документа; slate:product-sku-changed отправляется непосредственно на document.
| Событие | detail |
|---|---|
slate:open, slate:close |
{id, type}: тип drawer, dropdown, collapse или tabs; вкладки отправляют событие открытия |
slate:slide |
{id, index} |
slate:lightbox:change |
{index, item}: сменился кадр общего окна просмотра; событие всплывает от окна |
slate:filter |
{id, value}: кнопки-вкладки data-slate-filter скрыли карточки; пустое value — показаны все |
slate:quick-view-loaded |
{url}: содержимое быстрого просмотра товара вставлено в окно |
cart:changed |
{action, itemInfo, quantity, count}; действия added, updated, removed, cleared. У события от sync() — source: 'sync'; pending: true — промежуточный шаг составной операции, итоговое событие придёт следом |
wishlist:changed, compare:changed |
{id, action, count, ids}; действия added, removed, cleared, при очистке id: 0 |
slate:product-sku-changed |
{sku} |
product:sku-changed |
{sku, productId, productData} |
product:service-changed |
{serviceId, isEnabled, productId} |
product:service-variant-changed |
{serviceId, variantId, productId} |
slate:catalog-updated |
{url} |
lazyload:items-loaded |
{newItems, container, wrapper}: добавленные элементы, список и область пагинации |
category-filters:products-updated, category-sorting:products-updated, category-views:products-updated |
{newItems, container, source, url}; source — category-filters, category-sorting или category-views |
newItems — найденные DOM-элементы товаров, container — их список. Подписчик может заново обработать собственные элементы внутри этого контейнера. Для отписки передайте в .off() тот же объект функции, который был передан в .on().
Кроме событий темы, после выбора характеристики кнопкой и нажатия «+» или «−» форма товара получает обычное DOM-событие change. По нему пересчитывают свои блоки плагины, например «Гибкие скидки»; свой код может слушать его так же.
Данные и совместимость
В waTheme доступны адреса текущего приложения, сведения о теме, локализованные строки, CSRF и данные корзины. waTheme.cart существует и на сайте без магазина; проверяйте непустой cart_url, а не наличие объекта.
В waTheme.slate.settings по умолчанию передаётся только список настроек, читаемых JavaScript темы. Актуальный список находится в u-settings-keys.html. Флажок «Прочее → Настройки для скриптов → Публиковать все настройки темы» публикует все значения настроек, кроме ключа диагностики; формат {ключ: значение} при этом не меняется. Флажки передаются строками: '1' означает включено, '' — выключено; проверяйте их строго, например value === '1'. У списков и переключателей сравнивайте точное значение выбранного варианта, у выбора по картинке — полный путь изображения из theme.xml. Не подставляйте вместо них произвольные 0, true или короткое имя картинки. Параметры собственных элементов передавайте через их data-*, не рассчитывая на полный дамп настроек.
Названия некоторых событий, например cart:changed и product:sku-changed, могут совпадать с другой темой. Это не делает совместимыми её объекты: обращения к чужим библиотекам и хранилищам настроек нужно заменить средствами Slate. Штатное событие slate:catalog-updated удобно для повторной обработки элементов после фильтрации, сортировки и смены вида каталога.