Динамическая подмена контента на JavaScript для B2B-сайта
-
Динамическая подмена контента - это когда JavaScript меняет HTML на странице в реальном времени, не требуя перезагрузки. Для B2B-сайтов завода это критично: посетитель из поиска по Fanuc видит одно, из Heidenhain - другое, из контекста Яндекса - третье. Конверсия растёт не потому, что вы красивее соседей, а потому, что точнее отвечаете на запрос.
Когда человек переходит по ссылке с UTM-меткой или из определённого источника, скрипт сразу подменяет заголовок, картинку, текст кнопки, даже номер телефона. Никаких конструкторов, никаких платных сервисов - просто код, который лежит в вашем репозитории и работает на всех браузерах.
Почему JavaScript, а не GTM
Google Tag Manager - это удобно для маркетологов, но есть подвох. GTM добавляет задержку загрузки, работает через асинхронный скрипт, и иногда контент мелькает на странице перед подменой. Пользователь видит оригинальный заголовок, потом он меняется - это смотрится неловко и влияет на восприятие.
Прямой JavaScript, загруженный в head или в начале body, работает до того, как страница отрендерится. Контент уже изначально подменён - никаких миганий, никаких задержек. Плюс вы полностью контролируете логику: условия, переменные, обработку ошибок. Не привязаны к интерфейсу GTM и его ограничениям.
Дополнительный бонус - это работает даже если GTM отключится или заблокируется расширением в браузере. Критично для B2B, где много IT-шников с uBlock Origin.
- GTM: удобен для разовых тестов, но медленнее и зависит от третьей стороны
- JavaScript: полный контроль, быстрее, работает всегда
- Комбо: можно использовать оба параллельно для разных целей
Готовый скрипт: парсим UTM и меняем контент
Вот базовая структура, которая работает на любом сайте. Скрипт парсит URL, ищет нужный параметр и подменяет элементы на странице.
const contentConfig = { 'fanuc': { title: 'Оригинальные запчасти Fanuc для ЧПУ-станков', subtitle: 'Ремонт контроллеров 0i, 31i, iNC в 24ч', img: '/img/fanuc-parts.jpg', buttonText: 'Заказать запчасть Fanuc', phone: '+7 (495) 123-45-67' }, 'heidenhain': { title: 'Запчасти Heidenhain: энкодеры, платы, сервоприводы', subtitle: 'Гарантия 2 года на оригинал', img: '/img/heidenhain.jpg', buttonText: 'Диагностика Heidenhain', phone: '+7 (495) 765-43-21' }, 'siemens': { title: 'Компоненты Siemens: контроллеры и приводы', subtitle: 'Поддержка всех поколений S7', img: '/img/siemens.jpg', buttonText: 'Запросить предложение Siemens', phone: '+7 (495) 555-55-55' } }; function parseUrlParam(param) { const urlParams = new URLSearchParams(window.location.search); return urlParams.get(param) ? urlParams.get(param).toLowerCase() : null; } function applyDynamicContent() { // Ищем utm_source, utm_term или utm_keyword - используй в зависимости от логики кампании const key = parseUrlParam('utm_source') || parseUrlParam('utm_term') || parseUrlParam('utm_keyword'); if (key && contentConfig[key]) { const data = contentConfig[key]; // Подменяем заголовок const titleElement = document.querySelector('.hero h1'); if (titleElement) titleElement.textContent = data.title; // Подменяем подзаголовок const subtitleElement = document.querySelector('.hero p'); if (subtitleElement) subtitleElement.textContent = data.subtitle; // Подменяем изображение const imgElement = document.querySelector('.hero img'); if (imgElement) imgElement.src = data.img; // Подменяем текст кнопки const buttonElement = document.querySelector('.lead-form button'); if (buttonElement) buttonElement.textContent = data.buttonText; // Подменяем номер телефона везде на странице const phones = document.querySelectorAll('[data-phone]'); phones.forEach(el => { el.textContent = data.phone; }); } } // Запускаем при загрузке DOM if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', applyDynamicContent); } else { applyDynamicContent(); }Как это работает:
- Конфиг
contentConfigхранит все варианты контента, привязанные к ключам (в примере - названиям брендов) - Функция
parseUrlParamдостаёт значение параметра из URL applyDynamicContentищет нужные элементы на странице и меняет их содержимое- Скрипт срабатывает, как только браузер прочитал DOM
Подмена по геолокации и источнику трафика
Часто нужно подменять контент не только по UTM, но и по тому, откуда пришёл посетитель. Например, из поиска Яндекса - одна версия, из контекстной рекламы Google Ads - другая.
Даннные о источнике можно получить из
document.referrer- это URL страницы, с которой пришёл пользователь. Если referrer содержит слово ‘google’, значит пришёл из поиска Google. Если ‘yandex’ - из Яндекса.const refererConfig = { 'google': { title: 'Быстрая доставка от Google', cta: 'Поиск нашел вас!' }, 'yandex': { title: 'Проверено Яндексом', cta: 'Популярно в Яндексе' }, 'facebook': { title: 'Спецпредложение из Facebook', cta: 'Рекомендуется друзьями' } }; function applyByReferrer() { const referrer = document.referrer.toLowerCase(); let matchedConfig = null; for (const [source, config] of Object.entries(refererConfig)) { if (referrer.includes(source)) { matchedConfig = config; break; } } if (matchedConfig) { document.querySelector('h1').textContent = matchedConfig.title; document.querySelector('.cta-text').textContent = matchedConfig.cta; } } applyByReferrer();Это помогает, когда вы запускаете кампании в разных каналах и каждому нужно говорить по-своему:
- Из поиска - акцент на решение проблемы
- Из соцсетей - акцент на скорость и удобство
- Из объявлений - акцент на цену или срок
Продвинутый вариант: JSON-конфиг и асинхронная загрузка
Если кейсов много и скрипт становится громоздким, можно вынести конфиг в отдельный JSON-файл. Это удобнее для обновления и масштабирования.
// Загружаем конфиг из файла fetch('/config/dynamic-content.json') .then(response => response.json()) .then(config => { const utm = parseUrlParam('utm_source'); if (utm && config[utm]) { applyContent(config[utm]); } }) .catch(error => console.warn('Config load failed:', error)); function applyContent(data) { Object.keys(data).forEach(selector => { const element = document.querySelector(selector); if (element) { element.textContent = data[selector]; } }); }JSON-файл выглядит так:
{ "fanuc": { ".hero h1": "Запчасти Fanuc: быстро и надежно", ".hero p": "Оригинал с гарантией 2 года", ".cta-button": "Заказать Fanuc" }, "siemens": { ".hero h1": "Решения Siemens для вашего производства", ".hero p": "Полная совместимость со всеми моделями", ".cta-button": "Запросить S7" } }Преимущество такого подхода:
- Разделение: логика остаётся в JavaScript, данные - в отдельном файле
- Масштабируемость: легко добавлять новые кейсы без изменения кода
- Кэширование: браузер кэширует JSON, отдельный запрос работает быстро
- A/B-тестирование: можно менять контент на бэкенде, не трогая фронт
Чек-лист: как правильно внедрить
Прежде чем запускать в боевых условиях, проверь эти моменты:
- Селекторы должны быть уникальными: используй
data-*атрибуты или специальные классы типа.dynamic-title, не ловишь по общим.h1или.text - Проверь наличие элемента: перед тем как менять, убедись, что элемент вообще есть на странице (
if (element)) - Тестируй на разных браузерах: особенно на старых версиях Safari и IE, если ещё поддерживаешь
- Следи за производительностью: не заставляй скрипт перерисовывать весь DOM, парсь только нужное
- Логируй ошибки: добавь console.error для дебага, но удали перед продакшеном
- Версионируй конфиг: если меняешь структуру, добавь версию в JSON
Пункт Что проверить Зачем Селекторы Элементы находятся Скрипт не будет ловить нужные элементы Параметры URL Названия совпадают с кейсами Подмена не сработает без совпадения Производительность Скрипт не блокирует рендер Страница не будет тормозить Fallback Базовый контент загружается Если подмена не сработала, пользователь видит что-то Кроссбраузерность Работает в Chrome, Firefox, Safari Теряешь часть аудитории в IE На что ещё обратить внимание
Забывают про важный момент: кэширование браузером. Если у пользователя уже кэширована старая версия страницы, новый скрипт может не загрузиться. Добавь версию в URL скрипта или используй cache-busting параметры.
Ещё один подводный камень - производительность на мобильных. Не усложняй конфиг, иначе на старых Android-телефонах скрипт будет исполняться долго и замораживать интерфейс. Лучше использовать простые селекторы и минимум условий.
И последнее - совместимость с фреймворками. Если сайт написан на React, Vue или Angular, прямое манипулирование DOM может сломать реактивность. В таких случаях лучше передать данные подмены через props или store, чтобы фреймворк сам перерисовал компоненты.
Всё остальное - дело опыта. Запускайте на малом трафике, смотрите метрики, итерируйте. Конверсия обычно растёт на 20-40% даже с базовой подменой, потому что люди видят именно то, что ищут.
Здравствуйте! Похоже, вас заинтересовал этот пост, но у вас ещё нет аккаунта.
Надоело каждый раз пролистывать одни и те же посты? Зарегистрировав аккаунт, вы всегда будете возвращаться на ту же страницу, где были раньше, и сможете выбирать, получать ли уведомления о новых ответах (по электронной почте или в виде push-уведомлений). Вы также сможете сохранять закладки и ставить лайки постам, чтобы выразить свою благодарность другим участникам сообщества.
С вашими комментариями этот пост мог бы стать ещё лучше 💗
Зарегистрироваться Войти© 2022 - 2026 InvestSteel, Inc. Все права защищены.