Перейти к содержимому
Забрать бесплатно
Модификации выключены. Так эта страница выглядит на обычной Тильде.

Карта в Тильде: Яндекс.Карты в корзине и класс-исключение .t-map

29 июля 2026 11 МИН КОМАНДА MODKOD

Карту на Тильду ставят блоком или своим кодом. Кода блока в разобранном рантайме нет, зато есть карта пунктов выдачи в корзине — живая интеграция с Яндекс.Картами, видная целиком: от загрузки скрипта до кластеризации меток. По ней понятно, почему карта не появляется без ключа, почему она всегда по-русски и почему на телефоне её не сдвинуть одним пальцем. И там же лежит ошибка, из-за которой клиент выберет не тот пункт.

✓ ПРОВЕРЕНО 29 июля 2026 перепись провайдеров карт по 114 файлам JS: `ymaps` — 18 вхождений и все в модуле доставки, `maps.googleapis`, `google.maps`, `leaflet`, `mapbox`, `openstreetmap` — по 0, `navigator.geolocation` — 0, `loading="lazy"` — 0 при контрольных 75 вхождениях `IntersectionObserver`; `mapAppendScript`, `mapInit` и `mapAddPoints` выписаны из `tilda-delivery-1.0.min.js` целиком при контрольных 878 вхождениях `tcart|delivery`; обработка `.t-map` снята из `tilda-autoscale-1.0.min.js` — рантайм Тильды из 114 файлов JS и 37 файлов CSS, плюс 22 сохранённые страницы

Вопрос «как сделать карту в Тильде» распадается на два разных: поставить карту на страницу и заставить её вести себя правильно. На первый ответ короткий, на второй — интересный, потому что в рантайме Тильды есть одна полностью читаемая карта, и на ней видно все решения, которые придётся принять и вам.

Короткий ответ

Карту на страницу Тильда ставит блоком (его кода в разобранном рантайме нет — он приезжает постраничным бандлом) или вашим кодом. Единственный провайдер, с которым Тильда работает из кода, — Яндекс.Карты 2.1, и ровно в одном месте: карта пунктов выдачи в корзине. Google, Leaflet, Mapbox, OpenStreetMap — по нулю вхождений. Всё, что доказуемо про блок-карту, — что её контейнер носит класс t-map и что автомасштаб обходится с ним особым образом.

Что в рантайме есть и чего нет

что искалJS (114 файлов)где
ymaps18только tilda-delivery-1.0.min.js
api-maps.yandex2там же
maps.googleapis0—
google.maps0—
leaflet0—
mapbox0—
openstreetmap0—
t-map4только tilda-autoscale-1.0.min.js
navigator.geolocation0—
loading="lazy"0—
IntersectionObserver75контроль замера

Из этой таблицы сразу три практических вывода.

Google-карта на Тильде — это iframe. JS API Google в рантайме нет вовсе, значит управлять такой картой из кода нечем: ни метку добавить, ни центр сдвинуть, ни на клик подписаться. Всё, что можно, — вставить готовую рамку и задать ей размер.

Тильда не спрашивает геолокацию. Ни одного обращения к navigator.geolocation, то есть окно «разрешить доступ к местоположению» на странице Тильды не всплывёт никогда. Город для доставки определяется цепочкой restoreFromLastOrder → restoreFromLS → getCityFromGeoWithFailHandler — прошлый заказ, потом сохранённые поля, потом определение на стороне сервера. Разрешения у пользователя не просят.

Карту в рамке Тильда не отложит. loading="lazy" — ноль вхождений во всех файлах и во всех сохранённых страницах, при этом IntersectionObserver в рантайме используется 75 раз. Механика для отложенной загрузки есть, к рамкам она не применяется. Значит атрибут loading="lazy" на свой iframe ставите вы, и без него карта тянет свои килобайты сразу, даже если лежит в подвале.

Класс .t-map — единственное, что доказуемо про блок-карту

Имя класса встречается в одном файле, tilda-autoscale-1.0.min.js, и оба раза — как исключение. Сначала карту исключают из пересчёта высот:

document.querySelectorAll(sel + " [" + attr + "]").forEach(function (el) {
  if (!el.classList.contains("t-map")) {
    var v = el.getAttribute(attr);
    if (v) { var prop = c(el);
             el.style.setProperty(prop, "calc(" + v + "vh / var(" + name + ", 1))") }
  }
})

Потом ей отдельным проходом гасят масштаб:

function t_autoscale__processMaps() {
  var d = t_autoscale__getAutoscaleData(), sel = d.scaledRecordSelector, name = d.variableName;
  document.querySelectorAll(sel + " .t-map").forEach(function (el) {
    el.style.zoom = "calc(1 / var(" + name + ", 1))";
    …
  })
}

Читается это так: масштабировать карту вместе с остальным содержимым нельзя — поедут плитки и координаты клика, — поэтому Тильда компенсирует масштаб через zoom, а высоту в vh ей вообще не пересчитывает.

Практический вывод, которого нет ни в одной справке. Если вы вставляете свою карту в Zero Block с автомасштабом, дайте её контейнеру класс t-map. Иначе контейнер попадёт в общий пересчёт, карта отмасштабируется вместе со всем блоком — и клики начнут приходить не туда, куда вы ткнули. Класс здесь работает как объявление «я карта, меня трогать особым образом».

Живая карта: пункты выдачи в корзине

Дальше — единственная карта, которую видно целиком. Она собирается тремя методами модуля доставки, и разбор стоит того: те же вопросы встанут перед любой своей картой.

Загрузка скрипта: одна строка и один хардкод

mapAppendScript: function (t, r) {
  var n = document.createElement("script");
  n.src = "https://api-maps.yandex.ru/2.1/?apikey=" + (r || "") + "&lang=ru_RU";
  document.head.appendChild(n)
}

lang=ru_RU вписан насмерть. Запомните это место: через двадцать строк выяснится, что подсказку над той же картой Тильда переводит на тринадцать языков.

И (r || "") — если ключа нет, в адрес уйдёт apikey= без значения. Впрочем, до этого дело не доходит, потому что вызов стоит за проверкой:

r.deliveryState.ymapApiKey = t,
r.deliveryState.ymapApiKey && r.mapAppendScript("", t);

Ключ гасит всё. Тот же ymapApiKey && стоит перед mapInit("yandex"), перед mapAddPoints(...) и перед определением координат города. Нет ключа — нет скрипта, нет карты, нет метки, нет координат. Ни ошибки в консоли, ни сообщения пользователю: карта просто не появляется. Это ответ номер один на «почему в корзине нет карты».

Обратите внимание и на первый аргумент: вызов mapAppendScript("", t) передаёт пустую строку, а тело функции параметр t не читает. То же в mapAddPoints("", n, v). Два мёртвых параметра — след от заготовки под второго провайдера, которого так и не появилось.

Создание карты: четыре решения, все спорные

window.t_delivery__map = new ymaps.Map("delivery-yandex-map", {
  center: n.deliveryState.cityCoordinates || [55.76, 37.64],
  zoom: 8, controls: ["zoomControl"], minZoom: 3, maxZoom: 20
}, {
  yandexMapDisablePoiInteractivity: !0,
  suppressMapOpenBlock: !0
});

[55.76, 37.64] — Москва. Не определились координаты города — карта открывается в Москве, молча.

zoom: 8 — это масштаб области, не города: в кадре весь регион. Оправдано тем, что сразу после расстановки меток вызывается setBounds, — но первый кадр пользователь всё равно видит.

controls: ["zoomControl"] — из всех штатных элементов управления оставлен один. Ни поиска, ни линейки, ни кнопки «где я», ни полного экрана.

yandexMapDisablePoiInteractivity и suppressMapOpenBlock — выключены собственные объекты Яндекса и блок «открыть в Яндекс.Картах». Разумно: клик по чужой кофейне посреди выбора пункта выдачи только мешает.

Жест двумя пальцами: приём, который стоит забрать

Классическая беда карты на странице: пользователь листает страницу, палец попадает на карту, и вместо прокрутки страница замирает, а карта уезжает. Тильда решает это так:

var pane = window.t_delivery__map.panes.get("events").getElement(),
    hint = { EN: "Use two fingers to move the map",
             RU: "Чтобы переместить карту проведите по ней двумя пальцами",
             FR: "Utilisez deux doigts pour déplacer la carte",
             DE: "Verschieben der Karte mit zwei Fingern", … };  // всего 13 языков
Object.keys(style).forEach(function (k) { pane.style[k] = style[k] });
window.tStoreIsMobile && (
  window.t_delivery__map.behaviors.disable("drag"),
  window.ymaps.domEvent.manager.add(pane, "touchmove", function (e) {
    1 === e.get("touches").length && (
      pane.style.transition = "opacity .3s",
      pane.style.background = "rgba(0, 0, 0, .45)",
      pane.textContent = hint[window.t_delivery__browserLang] || hint.EN,
      pane.style.opacity = "1")
  }),
  window.ymaps.domEvent.manager.add(pane, "touchend", function () {
    pane.style.transition = "opacity .8s"; pane.style.opacity = "0"
  })
);

На мобильном перетаскивание одним пальцем отключается совсем (behaviors.disable("drag")), а при касании одним пальцем на карту наплывает затемнение с подсказкой: появление за 0.3 с, исчезновение за 0.8 с. Двумя пальцами карта двигается как обычно. Приём хороший, воспроизводимый на любой карте, и его стоит взять.

А вот языки: подсказка переведена на тринадцать языков, включая японский, китайский, казахский и латышский, и берётся по t_delivery__browserLang — который сначала считается из navigator.language, а потом, если у проекта задан язык, заменяется на него:

window.t_delivery__browserLang =
  (window.navigator.userLanguage || window.navigator.language).toUpperCase().slice(0, 2);
t_onReady(function () {
  var el = document.getElementById("allrecords"),
      lang = el ? el.getAttribute("data-tilda-project-lang") : "";
  lang && (window.t_delivery__browserLang = lang)
});

То есть подсказка над картой будет на языке проекта, а сама карта — со своими подписями, кнопкой зума и балунами — всегда на русском, потому что lang=ru_RU вписан в адрес скрипта. Латышская подсказка над русской картой — прямое следствие двух строк, которые писали разные люди.

Координаты города: порядок наоборот и тихий провал

xhr.open("GET", "https://geocode-maps.yandex.ru/1.x/?apikey="
  + this.deliveryState.ymapApiKey + "&format=json&geocode=" + query);
xhr.timeout = 3e4;

Ответ разбирается так:

try {
  var t;
  (t = e.response.GeoObjectCollection.featureMember[0].GeoObject.Point.pos.split(" "))
    && (t = [t[1], t[0]], r.deliveryState.cityCoordinates = t)
} catch (n) { console.error(n) }

Координаты меняются местами — и это не причуда Тильды, а свойство API: геокодер отдаёт pos как «долгота широта», а ymaps.Map ждёт [широта, долгота]. Перепутать здесь — классика; строка [t[1], t[0]] ровно об этом.

featureMember[0] берётся без проверки. Город не нашёлся — массив пуст, обращение к [0] бросает исключение, catch пишет его в консоль, cityCoordinates остаётся null, карта открывается в Москве. Пользователь при этом не узнаёт ничего.

Таймаут 30 секунд, и колбэк вызывается только при status === 200. Любой другой ответ и любой таймаут заканчиваются тем же: тишина и Москва.

Находка: пункт без координат сдвигает подписи у всех следующих

Вот расстановка меток, сокращённая до сути:

var coords = [];
points.forEach(function (p) {
  0 === p.coordinates[0] && 0 === p.coordinates[1] || coords.push(p.coordinates)
});
if (coords.length > 0) {
  for (var clusterer = new ymaps.Clusterer({ preset: "islands#circleIcon", … }),
           balloon = function (idx) {
             var p = points[idx];              // ← полный список
             …
             v += '… data-point-index=' + idx + ' onclick="window.tcart__chosePointOnMap(this)">…';
             window.tcart__chosePointOnMap = function (el) {
               var p = points[el.dataset.pointIndex];   // ← тоже полный список
               … i.deliveryState.pickupPointId = p.id …
             };
             return { balloonContentHeader: …, balloonContentBody: v, … }
           },
           marks = [], c = 0, d = coords.length; c < d; c++)
    marks[c] = new ymaps.Placemark(coords[c], balloon(c), preset(c));

Смотрите на последнюю строку. Координата берётся из отфильтрованного массива coords, а описание — из полного points по тому же индексу. Пока фильтр ничего не выбросил, индексы совпадают. Но фильтр здесь не случайный: Тильда сама проверяет координаты [0, 0], а такую проверку не пишут от нечего делать — значит пункты без координат в выгрузках бывают.

И как только один такой пункт выброшен, всё расходится на единицу. Метка стоит по координатам пункта №4, а в балуне показаны название, адрес, часы и телефон пункта №3. Нажатие «Выбрать» вызывает tcart__chosePointOnMap, который читает points[data-point-index] — тот же сдвинутый индекс, — и в заказ уходит пункт №3. Последний пункт списка при этом не получает метки вовсе: цикл идёт до coords.length, а он на единицу короче.

Оговорка честная: это прочитано в коде, а не увидено на живой корзине — выгрузки пунктов выдачи у нас нет, воспроизвести нечем. Но арифметика проверяется глазами, и последствие у неё денежное: клиент выбирает пункт рядом с домом, а получает предыдущий по списку.

Чему это учит в своём коде: фильтровать и описывать нужно один и тот же массив. Правильно было бы отфильтровать список пунктов целиком и дальше работать только с ним.

Из того же куска — две мелочи на будущее. geoObjects.removeAll() перед каждой отрисовкой: карта перерисовывается целиком, а не разницей. И window.tcart__chosePointOnMap переопределяется внутри цикла — на каждый пункт заново, хотя достаточно одного раза.

Что из этого делать

Ключ Яндекс.Карт — первое, что проверять. Если карты в корзине нет, дело почти наверняка в нём, и никакого сообщения об этом не будет.

Свою карту в масштабируемом Zero Block помечайте классом t-map. Тогда автомасштаб обойдётся с ней так, как обходится со штатной: без пересчёта высоты и с погашенным масштабом.

loading="lazy" на iframe ставьте руками. Тильда этого не делает нигде.

Помните про порядок координат. Геокодер Яндекса отдаёт «долгота широта», карта ждёт «широта долгота». Половина «карта показывает Африку» — про это.

Не листайте карту одним пальцем. Отключайте drag на мобильном и показывайте подсказку — приём из корзины Тильды переносится в свой код почти дословно.

Проверяйте пустые координаты до отрисовки, а не во время. Отфильтруйте список объектов целиком, и тогда никакой индекс не разъедется.

Откуда числа

Перепись провайдеров — поиск по 114 файлам JS, 37 файлам CSS и 22 сохранённым страницам, счёт по вхождениям, а не по строкам. Контроли печатались рядом: t_onFuncLoad — 271 по JS, data-record-type — 219 по страницам, tcart|delivery — 878 в модуле доставки, IntersectionObserver — 75 по JS.

mapAppendScript, mapInit и mapAddPoints выписаны из tilda-delivery-1.0.min.js (186.3 КБ) целиком; обработка .t-map — из tilda-autoscale-1.0.min.js, оба вхождения.

Что проверено и оказалось не тем, чем казалось: T175 — не карта. В CSS у него .t175__widepadding, .t175__title, .t175__descr, .t175__text, .t175__img — это блок с текстом и картинкой. И ни в одной из 22 сохранённых страниц нет iframe с картой: то есть разметку блока-карты нам сверять не с чем, и всё, что сказано выше про блок, держится на классе t-map в автомасштабе — не больше.

И общая оговорка. Отсутствие имени в наборе файлов не равно отсутствию его в Тильде: у Тильды отложенная привязка через t_onFuncLoad, и код блока может лежать в постраничном бандле, которого у нас нет. Поэтому «в Тильде нет блока-карты» здесь никто не утверждает — утверждается ровно то, что его кода нет в разобранном рантайме, а Google-карты нет и там, и в виде JS API вообще.

Вопросы по этой статье 4

Скорее всего не задан ключ API Яндекс.Карт. В коде всё, что связано с картой, стоит за одной проверкой: без `ymapApiKey` не грузится скрипт, не создаётся карта, не расставляются метки и не определяются координаты города. Ни ошибки, ни сообщения при этом нет — карта просто не появляется.