К содержимому
Параметры, хуки и интеграции

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 удобно для повторной обработки элементов после фильтрации, сортировки и смены вида каталога.