Почему в редакторе Тильды всё работает, а на сайте нет
Знакомая история: в редакторе мод стоит как надо, страница опубликована — и всё съехало. Или наоборот: на сайте работает, а в редакторе мёртво. Дело не в кэше и не в порядке загрузки. Рантайм Тильды сам проверяет режим страницы в нескольких местах и в редакторе делает другое: не ставит масштаб, не умножает высоту, подменяет адреса запросов и откладывает вызовы до готовности редактора. Все четыре места — цитатами из её кода.
Короткий ответ
Редактор, предпросмотр и опубликованная страница — три режима, и рантайм Тильды
их различает: data-tilda-mode на #allrecords равен edit, preview или
отсутствует. В редакторе не ставится --zoom, автовысота не умножается на
масштаб, ширина окна берётся из --page-view-width, а вызовы ждут готовности
редактора. Свой код должен считать «нет атрибута» опубликованной страницей.
Три режима и один атрибут
var a = document.getElementById('allrecords'),
l = a && 'edit' === a.getAttribute('data-tilda-mode');
void 0 === window.tn.isEditMode && (window.tn.isEditMode = l);
Так рантайм Zero Block один раз запоминает режим в window.tn.isEditMode. Дальше
всё опирается на эту переменную.
| Режим | data-tilda-mode |
|---|---|
| редактор | edit |
| предпросмотр | preview |
| опубликованная страница | атрибута нет |
Третью строку я проверил по четырём целиком снятым страницам Тильды: строка
data-tilda-mode встречается в них только внутри скриптов, которые её читают, а
на самом #allrecords атрибута нет ни на одной. Вот как выглядит настоящий
#allrecords опубликованной страницы:
<div id="allrecords" class="t-records" data-tilda-project-id="1717"
data-tilda-page-id="75717" data-tilda-formskey="68ac3d64…"
data-tilda-project-lang="RU" data-tilda-root-zone="net"
data-tilda-project-country="AE">
Отсюда первое практическое правило. Проверка вида if (mode === 'published') не
сработает никогда: такого значения не существует. Правильно наоборот — считать
опубликованным всё, что не редактор и не предпросмотр. Ровно так это делает
каталог:
var a = document.getElementById('allrecords').getAttribute('data-tilda-mode');
r.isPublishedPage = 'edit' !== a && 'preview' !== a;
Причина первая: масштаб. Три места
Самая частая причина «в редакторе на месте, на сайте съехало» — масштабирование Zero Block. Оно намеренно устроено в редакторе по-другому, и в трёх местах сразу.
Переменная --zoom в редакторе не ставится.
return _ && !window.tn.isEditMode && _.style.setProperty('--zoom', l.toString()), l;
Коэффициент вычислен, в window.tn.ab<id>.scaleFactor записан — а в CSS не ушёл.
Свой мод, который читает --zoom у записи, в редакторе получит пустую строку. Если
он на этом строит арифметику, в редакторе получится NaN — а на сайте всё
посчитается. Обратный случай, тот, который выглядит как «в редакторе не работает».
Ширина окна в редакторе — не ширина окна.
function t396__getBlockEditorWidth() {
if (!window.tn.isEditMode) return 0;
var e = window.getComputedStyle(document.body).getPropertyValue('--page-view-width');
return e && '100%' !== e ? parseInt(e, 10) : 0;
}
Ноль здесь значит «спрашивай окно», иначе берётся ширина области просмотра
редактора. Эта функция стоит первой в обеих функциях ширины —
t396__getTNWindowWidth и t396__getCSSWindowWidth. То есть в редакторе
разрешение выбирается по ширине рамки просмотра, а не браузера, и переключатель
устройств в редакторе честно меняет ту же величину, которую видит рантайм. Свой
код, который сам смотрит window.innerWidth, в редакторе будет расходиться с
Тильдой на всю разницу между рамкой и окном.
Автовысота в редакторе не умножается на масштаб.
'window' !== t396_ab__getFieldValue(t, 'upscale') || window.tn.isEditMode ||
(_ *= t396__getCurrentScaleFactor(t.getAttribute('data-artboard-recid')));
Артборд с автовысотой и масштабированием по окну в редакторе получит высоту без коэффициента, на сайте — с. Это буквальное «после публикации блок стал другой высоты».
И четвёртое, из кода самой страницы. На опубликованной странице лежит инлайновый скрипт, который вычисляет высоту центрированного артборда — и работает только вне редактора:
var _ = 'edit' === window.allrecords.getAttribute('data-tilda-mode'),
c = 'center' === t396_getFieldValue(i, 'valign', n, r), …
if (!_ && c && !s && !w && g && !u) {
var h = parseFloat((a / n).toFixed(3)), v = Math.floor(parseInt(g, 10) * h) + 'px';
i.style.setProperty('--initial-scale-height', v);
…
}
Пять условий вместе с режимом: вертикальное выравнивание по центру, масштаб не по
сетке, высота задана не в vh, высота вообще задана, и браузер не Opera
(последнее — буквально в её коде). Совпало всё — на сайте появляется
--initial-scale-height, которой в редакторе нет.
Причина вторая: в редакторе вызовы ждут
Весь отложенный код Тильда пропускает через одну функцию, и в редакторе у неё другое условие готовности:
function t_onFuncLoad(t, e, n) {
var o = 15e3, i = t_checkIsEditMode(),
r = function () { return !i || i && t_checkEditorIsReady() },
a = function (e) { return 'function' == typeof window[e] || 'object' == typeof window[e] };
if (a(t) && r()) e();
else {
var l = Date.now(), d = new Error(t + ' is undefined'), s = function () { throw d };
…
}
}
function t_checkEditorIsReady() {
if (!t_checkIsEditMode()) return !1;
var e = document.body.getAttribute('data-ready-status');
return !!(t = e && 'ready' === e);
}
На сайте достаточно, чтобы функция появилась. В редакторе — чтобы ещё и body
получил data-ready-status="ready". Отсюда два наблюдаемых следствия. В редакторе
инициализация приходит позже, чем на сайте, и мод, который ставит обработчики
рядом с блоком, может увидеть не тот DOM. И знакомая ошибка в консоли редактора
<имя функции> is undefined — это тот самый throw: через пятнадцать секунд
после readyState === 'complete', если функции так и нет.
Порог 15 секунд — тот же на сайте. Разница только в том, чего именно ждут.
Причина третья: в редакторе другая сеть
Три места, где в редакторе запросы уходят не туда или не уходят вовсе.
Промокод. Проверка не делается, вместо неё сообщение:
var d = r.getAttribute('data-tilda-mode');
if ('edit' !== d && 'preview' !== d) { … c.open('POST', 'https://' + window.tildaForm.endpoint + '/payment/promocode/', !0) … }
else alert(t_input_promocode__getLangText('publish'));
Русский текст в её словаре: «Пожалуйста, опубликуйте страницу, чтобы проверить функциональность активации промокода». То есть в редакторе промокод не проверяется принципиально, и человек видит это в модальном окне браузера. Свой код, который ждёт результата проверки, в редакторе не получит его никогда.
Магазин. Все запросы товаров подменяются:
var _ = 'https://' + window.t_store_endpoint + '/api' + (i ? '/getrelevantproducts/' : '/getproductslist/');
if (!r.isPublishedPage) {
var f = window.location.hostname.split('.'), y = 'tilda.' + f[f.length - 1];
u.projectid = … getAttribute('data-tilda-project-id');
_ = 'https://' + y + '/projects/store' + (i ? '/getrelevantproducts/' : '/getproductslist/');
}
На сайте — store.tildaapi.com/api/…, в редакторе — tilda.<зона>/projects/store/…
с идентификатором проекта в параметрах. Так же подменяются /getproductsbyuid/ и
/getfilters/. Мод, который перехватывает ответ магазина по адресу, в другом
режиме перестаёт срабатывать — просто потому, что адрес другой.
Ссылки на товар. Разбор адреса включается только на опубликованной странице:
r.isPublishedPage && t_store_checkUrl(r, t);
Значит открыть в редакторе карточку по прямой ссылке ?tproduct=… нельзя, и это
не поломка.
Как отличать режим в своём коде
function tildaMode() {
var root = document.getElementById('allrecords');
var mode = root && root.getAttribute('data-tilda-mode');
if (mode === 'edit') return 'editor';
if (mode === 'preview') return 'preview';
return 'published'; // атрибута нет — это и есть опубликованная страница
}
Дальше — решение, а не проверка. Для большинства модов правильный ответ: в редакторе не работать вовсе.
if (tildaMode() !== 'published') return; // в редакторе мод молчит
Причины две, обе из разделов выше. Мод, который двигает элементы или считает
геометрию, в редакторе будет спорить с самим редактором: у него другая ширина
окна, нет --zoom и другая высота артборда. А наблюдать за результатом всё равно
надо на опубликованной странице — по замерам выше редактор просто не то место, где
геометрия совпадает с настоящей.
Обратное правило тоже есть. Если мод обязан работать в редакторе (например,
подсвечивает что-то автору), то опираться нужно на window.tn.isEditMode и
t_onFuncLoad, а не на свои таймауты: только они знают про data-ready-status.
Что проверить у себя
// 1. Какой сейчас режим — и есть ли атрибут вообще
document.getElementById('allrecords').getAttribute('data-tilda-mode');
// 2. Что об этом думает сама Тильда
window.tn && window.tn.isEditMode;
// 3. Поставлен ли масштаб записи (в редакторе будет пусто)
[...document.querySelectorAll('[id^="rec"]')]
.map(r => r.id + ' → ' + (r.style.getPropertyValue('--zoom') || '(нет)'))
.filter(s => !s.endsWith('(нет)'));
// 4. Совпадает ли ширина, по которой считает Тильда, с шириной окна
window.tn && window.tn.window_width + ' против ' + window.innerWidth;
// 5. Область просмотра редактора — источник расхождения
getComputedStyle(document.body).getPropertyValue('--page-view-width');
Если в третьей проверке пусто, а страница открыта не в редакторе — масштабирование
у артборда просто не включено, и --zoom тут не при чём. Если в четвёртой числа
разъехались, а вы не в редакторе — смотрите на отступы по оси X: их вычитает
t396__getAxisXPadding.
Про то, как масштаб Zero Block ломает трансформы, отдельно: автомасштаб и transform. Свободные моды библиотеки — в каталоге. Если мод должен одинаково работать и в редакторе, и на сайте — это отдельная работа под задачу: мод под задачу.