Insights / Технические материалы
Техническая схема передачи контекста из CTA услуги в форму обратной связи
Схема реализации, которая передаёт в форму контекст, прочитанный на странице услуги. Рассматриваются мини-CTA в Astro, контракт URL-параметров, начальный выбор категории, prefill темы, многоязычные URL, измерение GA и проверка сгенерированного HTML.
Содержание
- Цель не только в сокращении ввода
- Вынести ответственность в компонент CTA
- Сделать URL-параметры контрактом с формой
- Хранить таблицу классификации в форме
- Добавить data-атрибуты к option
- Выполнить prefill на клиенте
- Перейти к форме с помощью hash
- Почему не добавлено hidden-поле
- Подход для многоязычных сайтов
- Проверить сгенерированный HTML
- Разделение ролей с ИИ-чатом
- Итог
Когда пользователь на странице услуги думает «хочу спросить об этом», простая отправка к форме теряет часть контекста.
Ему нужно снова выбрать тип услуги и написать тему. Принимающая сторона тоже не может легко понять до чтения сообщения, относится ли оно к веб-разработке, эксплуатации серверов или Aceserver.
На сайте Acecore мы улучшили этот путь в PR, который передаёт целевую услугу из CTA в форму. Здесь решение представлено не только как реализация на Astro, но и как схема пути, пригодная для других сайтов.
Цель не только в сокращении ввода
Задача не сводится к автоматическому заполнению полей, чтобы форма казалась проще.
Главное — корректно передать возникший на странице контекст в форму и процесс обработки на принимающей стороне.
| Точка зрения | Что требуется улучшить |
|---|---|
| Пользователь | Не выбирать заново услугу, которую он читал |
| Форма | Инициализировать категорию и тему согласно обращению |
| Приём | Классифицировать цель только по категории |
| Измерение | Отследить, с какого CTA началось обращение |
| Многоязычный путь | Отправить на контактный URL нужной locale |
Внешне это маленький CTA, но проектирование охватывает CTA, URL, форму, перевод, измерение и обработку обращения.
Вынести ответственность в компонент CTA
В конце каждого раздела размещается CTA «Проконсультироваться об этой услуге».
Не следует повторять одну и ту же генерацию ссылки и атрибуты GA прямо в каждом разделе. При семи услугах логика повторяется семь раз, и отдельные копии легко пропустить при изменении текста или спецификации URL.
Поэтому мы создали ServiceSectionActions, который объединяет контактный CTA.
---
import Icon from "./Icon.astro";
import { t, getLocalizedUrl, type Locale } from "../i18n";
interface Props {
locale: Locale;
gaLabel: string;
gaLocation: string;
serviceKey: string;
}
const { locale, gaLabel, gaLocation, serviceKey } = Astro.props;
const u = (path: string) => getLocalizedUrl(path, locale);
const contactUrl = `${u("/contact/")}?category=service&service=${encodeURIComponent(serviceKey)}#contact-form`;
---
<a
href={contactUrl}
class="ac-btn-outline gap-2 text-sm sm:w-auto"
data-ga-event="cta_click"
data-ga-label={gaLabel}
data-ga-location={gaLocation}
data-ga-destination={contactUrl}
>
<Icon name="message-circle" class="text-sm" />
{t(locale, "pages.services.miniCta")}
</a>
У компонента три обязанности:
- Сгенерировать контактный URL для locale
- Поместить service key в URL-параметр
- Хранить label и location для измерения GA
CTA — это точка действия и измерения, а не только интерфейс. data-ga-label и data-ga-location позволяют позже определить, от какой услуги началось обращение.
Сделать URL-параметры контрактом с формой
Значения передаются через URL-параметры.
/contact/?category=service&service=web#contact-form
Важно не помещать в URL отображаемую подпись.
Текст Webサイト制作・運用について зависит от перевода, вариаций написания и будущих переименований. URL содержит только короткий service key, например web или server.
| Параметр | Роль |
|---|---|
category |
Обозначает вход для обработки как обращения по услуге |
service |
Стабильный key целевой услуги |
| hash | Используется для прокрутки к форме |
Пользователь может редактировать параметры. Поэтому форма не отправляет URL-значение напрямую, а сопоставляет его с существующим option.
Хранить таблицу классификации в форме
Форма хранит категории услуг в массиве.
const serviceCategoryOptions = [
{
key: "server",
value: "サーバー構築・運用について",
label: t(locale, "pages.contact.formCategoryServiceServer"),
subject: t(locale, "pages.services.server.title"),
},
{
key: "web",
value: "Webサイト制作・運用について",
label: t(locale, "pages.contact.formCategoryServiceWeb"),
subject: t(locale, "pages.services.web.title"),
},
];
key, value, label и subject имеют разные роли.
| Поле | Роль |
|---|---|
key |
Стабильный идентификатор для поиска по URL-параметру |
value |
Категория, получаемая при отправке формы |
label |
Переведённый option, видимый на экране |
subject |
Название услуги для начального заполнения темы |
На многоязычном сайте label переводится для locale. value служит внутренней классификации, поэтому остаётся стабильным японским значением.
Решение зависит от продукта. Если CRM поддерживает многоязычные категории, value тоже может зависеть от locale. Для простого приёма отделение видимой подписи от отправляемого значения удобнее.
Добавить data-атрибуты к option
Select выводит option для каждой услуги.
<select id="category" name="category" required>
<option value="" disabled selected>
{t(locale, "pages.contact.formCategoryPlaceholder")}
</option>
<option value="サービス全般について">
{t(locale, "pages.contact.formCategoryService")}
</option>
{
serviceCategoryOptions.map((option) => (
<option
value={option.value}
data-service-key={option.key}
data-service-subject={option.subject}
>
{option.label}
</option>
))
}
</select>
data-service-key сравнивается с service в URL. data-service-subject используется для темы.
URL-значение не помещается напрямую в category.value. Выбор существующего option не позволяет неизвестному service key или неверному значению попасть в отправляемые данные.
Выполнить prefill на клиенте
Небольшой скрипт инициализирует форму после загрузки.
function initContactServicePrefill() {
const form = document.getElementById("contact-form");
if (!form || form.dataset.servicePrefillInitialized === "true") return;
form.dataset.servicePrefillInitialized = "true";
const url = new URL(window.location.href);
const requestedCategory = url.searchParams.get("category");
const requestedService = url.searchParams.get("service") || "";
const category = document.getElementById("category");
const subject = document.getElementById("subject");
if (
requestedCategory === "service" &&
category instanceof HTMLSelectElement
) {
const serviceOption = Array.from(category.options).find((option) => {
return option.dataset.serviceKey === requestedService;
});
category.value = serviceOption?.value || "サービス全般について";
category.dispatchEvent(new Event("input", { bubbles: true }));
category.dispatchEvent(new Event("change", { bubbles: true }));
if (
serviceOption &&
subject instanceof HTMLInputElement &&
!subject.value.trim()
) {
const template = form.dataset.serviceSubjectTemplate || "{service}";
const serviceName =
serviceOption.dataset.serviceSubject ||
serviceOption.textContent?.trim() ||
"";
subject.value = template.replace("{service}", serviceName);
}
}
}
Четыре важных момента:
- Проверять
data-service-prefill-initialized, чтобы избежать двойной инициализации - Обрабатывать только при
category=service - Для неизвестного service key возвращаться к
サービス全般について - Заполнять тему только тогда, когда она пуста
Последний пункт особенно важен. Если тема сохранилась после возврата назад или автозаполнения, её перезапись ухудшит опыт.
При Astro View Transitions или клиентской навигации инициализация также выполняется на astro:page-load.
document.addEventListener("astro:page-load", initContactServicePrefill);
initContactServicePrefill();
Перейти к форме с помощью hash
URL CTA содержит #contact-form.
/contact/?category=service&service=web#contact-form
Контактная страница может включать FAQ, LINE, пояснения и другие способы связи, поэтому посетителя из CTA естественно сразу переместить к форме.
При инициализации формы нужно учитывать момент прокрутки. requestAnimationFrame выполняет её после отрисовки элемента.
if (window.location.hash === "#contact-form") {
window.requestAnimationFrame(() => {
form.scrollIntoView({ block: "start" });
});
}
Это небольшое поведение, но несовпадение намерения CTA и видимой позиции формы сбивает пользователя. URL, предварительный выбор и прокрутка проектируются вместе.
Почему не добавлено hidden-поле
Мы не добавляли hidden-поле 相談対象サービス.
Целевая услуга должна определяться только по категории.
Дополнительные поля создают дополнительные проверки:
- Выводить ли его в уведомлении?
- Добавлять ли столбец в панели или таблице?
- Влияет ли оно на шаблоны автоответа?
- Обрабатывается ли CRM или Webhook?
- Как разделять многоязычные названия и принимаемые значения?
Если информация выражается существующими полями, отсутствие нового поля делает операции стабильнее. お問い合わせ種別 делится на общие и отдельные сервисные категории.
Hidden-поле может быть оправдано для выбора нескольких услуг, хранения идентификатора кампании или отдельного поля CRM.
Подход для многоязычных сайтов
Разделение трёх значений предотвращает путаницу.
| Тип | Пример | Зависит от locale |
|---|---|---|
| URL key | web, server, aceserver |
Нет |
| Видимая подпись | About Website Design и т. п. |
Да |
| Отправляемое значение | Webサイト制作・運用について |
Зависит от операций |
URL key лучше не переводить: он используется при обмене ссылками, измерении и сопоставлении в форме.
Видимая подпись всегда переводится, поскольку её читает пользователь.
Отправляемое значение определяется операциями. Здесь используются стабильные японские значения. Разделение многоязычного отображения и внутренней обработки упрощает управление формой.
Поток перевода также описан в статье Как вести многоязычный блог с Sveltia CMS.
Проверить сгенерированный HTML
Недостаточно смотреть только на компонент. После build следует подтвердить фактический вывод ссылок и option.
Мы проверяли:
- Семь отдельных CTA в
/services/ - Каждый CTA содержит
?category=service&service=...#contact-form - Семь option с
data-service-keyв/contact/ - Присутствуют
サービス全般についてи отдельные категории - Hidden-поле
相談対象サービスотсутствует
Например, можно использовать rg.
rg -n "category=service&service=.*#contact-form" dist\services\index.html
rg -n "data-service-key" dist\contact\index.html
rg -n "相談対象サービス" dist\contact\index.html
Последняя проверка подтверждает отсутствие того, что не должно появляться. Изменение формы проверяет не только добавленные элементы, но и намеренно отсутствующие.
Разделение ролей с ИИ-чатом
Этот путь хорошо сочетается с технической схемой ИИ-чата для обращений, но роли различаются.
| Путь | Сильная сторона |
|---|---|
| ИИ-чат | В диалоге определить, по какой услуге обратиться |
| CTA услуги | Передать контекст читаемой услуги в форму |
| Форма | Принять официальное обращение и сохранить запись |
ИИ-чат полезен, пока пользователь сомневается. Если он закончил читать страницу и решил обратиться по этой услуге, естественнее сразу отправить его к форме без дополнительного разговора.
Не следует назначать всем путям одну роль. Диалог, CTA и форма используются в зависимости от состояния пользователя.
Итог
Передача контекста от страницы услуги к форме эффективнее, чем кажется по небольшому визуальному изменению.
Важные моменты:
- Вынести CTA в компонент и объединить генерацию URL и атрибуты измерения
- Использовать в URL стабильный service key вместо видимой подписи
- Сопоставлять service key с option в форме
- Разделять отправляемое значение, label и имя услуги для темы
- Для неизвестного service key возвращаться к общим вопросам
- Заполнять тему только тогда, когда она пуста
- Классифицировать по категории без дополнительного hidden-поля
- После build проверять число ссылок и option и отсутствие ненужных полей
Улучшение формы — не только сокращение полей. Передача прочитанного пользователем контекста до принимающей стороны упрощает реальную обработку обращений.
Поток передачи контекста из CTA услуги в форму
Service
Назначить CTA каждого раздела service key.
URL
Передать через контракт URL вида /contact/?category=service&service=web#contact-form.
Form
Инициализировать в форме подходящую категорию и тему.
Ops
Принимающая сторона определяет контекст услуги только по категории.
Разница при соединении CTA с формой
Просто отправить к форме
- Пользователь повторно выбирает ту же услугу
- Тема остаётся пустой и суть обращения непонятна
- Принимающей стороне приходится читать сообщение, чтобы определить услугу
- Измерение отдельных CTA услуг остаётся неоднозначным
Передать контекст
- Категорию можно предварительно выбрать по service key из CTA
- Название услуги можно подставить в тему и структурировать обращение
- Принимающая сторона классифицирует обращение по категории
- GA label и URL-параметры упрощают анализ каждого CTA
Проверка проектирования при внедрении
- Использовать в URL только короткие и стабильные service keys
- Использовать для отправки операционно стабильные значения, а не видимые пользователю подписи
- Для неизвестного service key возвращаться к общим вопросам об услугах
- Заполнять тему только тогда, когда она пуста
- До добавления hidden-поля проверить, достаточно ли существующей категории для классификации
- Генерировать на сервере контактный URL для каждой locale
- Добавить CTA параметры GA label и location для измерения эффективности
- После build проверить в HTML число CTA и option, а также наличие или отсутствие hidden-полей
Связанные страницы
Часто задаваемые вопросы
Почему не отправлять целевую услугу в hidden-поле?
Чтобы не увеличивать число полей для принимающей стороны и классифицировать обращение только по существующей категории. Каждое дополнительное поле также добавляет проверки в операциях и шаблонах уведомлений.
Безопасно ли изменение URL-параметров?
Неизвестный service key возвращается к общим вопросам об услугах. Отправляемое значение выбирается среди option формы, поэтому само значение URL напрямую не отправляется.
Как работать с этим на многоязычном сайте?
Генерируйте адрес CTA для каждой locale и переводите видимые подписи формы. Стабильные японские значения классификации в отправляемых данных помогают сохранять единообразие приёма.