与网站访客互动是建立信任和推动转化的最强大方式之一。一个精心设计的表单,就像是您与受众之间的桥梁——帮助将流量转化为有价值的潜在客户或真正的客户。
这就是为什么表单跟踪如此重要。通过跟踪表单提交,我们可以衡量互动情况、识别转化率,并了解受众是如何与网站进行互动的。
更棒的是,这些数据还能通过 Google Tag Manager (GTM) 与各大营销平台共享。借助 GTM 驱动的跟踪,营销平台能够更深入地洞察客户行为——从而优化营销活动,带来更多流量、更精准的定位,以及更高的投资回报率 (ROI)。
从今天开始跟踪您的表单,释放网站流量的真正潜力!
通过页面 URL 的表单提交跟踪
跟踪表单提交最简单、最有效的方法之一就是使用页面 URL。
许多网站在用户填写表单后,会跳转到一个带有“感谢”信息的新页面。
例如,当访客提交表单后,他们可能会被重定向到:
👉 https://yourwebsite.com/thank-you
这使得转化跟踪变得非常容易——因为每一次访问“感谢页面”,都代表一次成功的表单提交。
在 Google Tag Manager (GTM) 中进行设置的方法:
-
进入 GTM 的 触发器 (Triggers)。
-
点击 新建触发器 (New Trigger)。
-
根据您的网站设置,选择合适的 新触发器 (New Trigger)。
![]()
Page View → 在页面开始加载时立即触发。
DOM Ready → 当页面结构(HTML)完全加载完成时触发,但图像/脚本可能仍在加载中。
Window Loaded → 当页面上的所有内容(包括图片、CSS 和脚本)完全加载后触发。
你可以这样简单记忆:
Page View = 开始,DOM Ready = 中间,Window Loaded = 完成。
在这个阶段,你会看到 三个选项 来设置 Google Tag Manager (GTM) 中的触发器。你可以选择最适合你跟踪需求的选项——但在本例中,我们将使用 Window Loaded 来实现更可靠的跟踪。
配置方法如下:
-
选择 Window Loaded 作为触发器类型。
-
将 Page Path(页面路径) 设置为 equals(等于)。
-
输入最终链接:thank_you。
就是这样!你的表单提交触发器现在已经完全设置完成。每当用户提交表单并进入“感谢页面”时,GTM 就会准确捕捉到该事件。

通过按钮点击进行表单提交跟踪
跟踪表单提交的另一种方法是 监控表单按钮的点击。
通过启用以下变量:
-
Click ID(点击 ID)
-
Click Text(点击文本)
-
Click Classes(点击类名)
当用户点击表单的提交按钮时,Google Tag Manager (GTM) 可以在 Data Layer 捕获有价值的详细信息。
有了这些数据,你就可以使用 GTM 的 “所有元素(All Elements)”触发器类型 来设置表单跟踪。


然而,通过按钮点击来跟踪表单 并不总是最可靠的方法。
为什么?因为如果我们仅在按钮点击时设置触发器,每当访客点击按钮时,标签就会被触发——即使表单未成功提交也会触发。这可能导致数据不准确,并在分析表单转化时产生误导。
这就是为什么 仅依赖按钮点击跟踪 并不是衡量真实表单提交的理想方法。
通过元素可见性触发器进行表单提交跟踪
在“感谢页面”方法之后,另一种 有效的表单提交跟踪方式 是使用 Google Tag Manager (GTM) 中的 元素可见性触发器 (Element Visibility Trigger)。
许多网站在表单提交后不会跳转到新页面。相反,它们会直接在同一页面上显示确认信息(例如“感谢您的提交”)。在这种情况下,元素可见性触发器 是最合适的解决方案。
工作原理如下:
-
提交表单后,找到显示的确认信息或文本行(例如,“Thank You” 文本)。
-
右键点击该元素,选择 检查 (Inspect) 打开开发者工具。

现在,选择你识别出的 CSS 类。然后进入 Google Tag Manager (GTM) 并按照以下步骤操作:
-
进入 触发器 (Triggers)
-
点击 新建触发器 (New Trigger)
-
在 触发器类型 (Trigger Type) 选项中,选择 元素可见性 (Element Visibility)

在 元素可见性触发器 (Element Visibility Trigger) 设置中,请按照以下步骤操作:
-
在 选择方法 (Selection Method) 中,选择 CSS Selector。
-
将收集到的 CSS 类 粘贴到选择器字段中。
-
别忘了在类名前加上“.”(点号)。
原因: 在 CSS 中,点号(.)用于选择类。如果没有它,GTM 将无法正确识别该元素。 -
将 最大可见百分比 (Maximum Percent Visible) 设置为 2%。
原因: 感谢信息通常很小,即使屏幕上仅有 2% 可见,GTM 也能检测到。这确保触发器能被触发,而无需整个元素完全显示。 -
勾选 观察 DOM 变化 (Observe DOM Changes) 选项。
原因: 许多确认信息是在表单提交后动态加载的。启用此选项后,GTM会持续观察页面中新出现的元素,确保即使感谢信息在页面初次加载时不存在,触发器仍能正常工作。
通过此设置,你的 元素可见性触发器 将能够 准确检测表单提交事件 并可靠触发标签,而不会产生误报。
通过监听器进行表单跟踪
我们已经探讨了几种表单提交跟踪的方法——使用 感谢页面 (Thank-You Pages)、按钮点击 (Button Clicks) 和 元素可见性 (Element Visibility)。但是在某些情况下,这些方法可能 无法适用于某些表单。
这时,事件监听器 (Event Listeners) 就成为了改变游戏规则的解决方案。
例如,对于像 Contact Form 7(WordPress 插件) 这样的插件,我们可以设置自定义监听器来 可靠地捕获表单提交事件。这种方法可以确保即使传统方法失效,我们也不会错过任何跟踪数据。
下面我们来演示如何在 Google Tag Manager (GTM) 中为 Contact Form 7 设置监听器:
-
进入 GTM 的 标签 (Tags) 部分。
-
创建一个专门用于监听器的新标签。
-
复制并粘贴提供的代码。
<script> document.addEventListener( 'wpcf7mailsent', function( event ) { window.dataLayer.push({ "event" : "cf7submission", "formId" : event.detail.contactFormId, "response" : event.detail.inputs }) }); </script>
4. 将触发器设置为 “所有页面 (All Pages)”,以确保监听器始终处于激活状态
这种方法可以保证 无缝的表单跟踪,即使其他方法无法正常工作时,也能可靠捕获数据。

此脚本是 Contact Form 7 的监听器。
它会等待 wpcf7mailsent 事件(表单成功提交)。
触发后,它会将数据推送到 GTM 的 dataLayer:
-
event:自定义名称(cf7submission)
-
formId:提交的表单 ID
-
response:表单输入内容
当代码成功安装后,每当有 Contact Form 7 表单提交时,你会在 Data Layer 中看到新的推送 “event”。

就这样!有了这些数据,你现在可以 完成对 “Contact Form 7” 的表单跟踪。
使用 GTM 进行 HubSpot 表单跟踪
HubSpot 表单 在全球也被广泛使用——就像 Contact Form 7 一样,它们需要 监听器 (Listener) 才能实现可靠跟踪。传统方法(如页面 URL 或元素可见性)无法以专业的方式捕捉 HubSpot 表单的提交数据。
解决方案? 我们将使用相同的 监听器设置流程 来准确跟踪 HubSpot 表单。下面我们一起操作:
-
进入 GTM 的 标签 (Tags) 部分。
-
创建一个专门用于监听器的新标签。
-
复制并粘贴提供的代码。
<script type="text/javascript"> window.addEventListener("message", function(event) { if(event.data.type === 'hsFormCallback' && event.data.eventName === 'onFormSubmitted') { window.dataLayer.push({ 'event': 'hubspot-form-success', 'hs-form-guid': event.data.id }); } }); </script>
将触发器设置为 “所有页面 (All Pages)”,以确保监听器始终处于激活状态。
![]()
-
当有人填写并提交表单时,HubSpot 会返回一条 “消息”。
-
脚本会“监听”这条消息,然后告诉 Google Tag Manager 的 Data Layer 表单已成功提交。
-
它还会发送唯一的 表单 ID,让你明确知道是哪一个表单被填写了。
同样的方式,你也可以跟踪 Gravity Form 和任何 Ajax 表单。
代码成功安装后,每当有表单提交时,你会在 Data Layer 中看到一条新的记录。
Gravity Form 跟踪:
<script> jQuery(document).ready( function() { jQuery(document).bind('strattic-form-success', function(e){ var formId = e.detail.form.id var formDetails = e.detail.formattedData window.dataLayer.push({ 'event' : 'gravityFormSubmission', 'gfformID' : formId, 'gfformSubmission' : formDetails }); }); }) </script>
任何 Ajax 表单跟踪:
<script id="gtm-jq-ajax-listen" type="text/javascript"> (function() { 'use strict'; var $; var n = 0; init(); function init(n) { // Ensure jQuery is available before anything if (typeof jQuery !== 'undefined') { // Define our $ shortcut locally $ = jQuery; bindToAjax(); // Check for up to 10 seconds } else if (n < 20) { n++; setTimeout(init, 500); } } function bindToAjax() { $(document).bind('ajaxComplete', function(evt, jqXhr, opts) { // Create a fake a element for magically simple URL parsing var fullUrl = document.createElement('a'); fullUrl.href = opts.url; // IE9+ strips the leading slash from a.pathname because who wants to get home on time Friday anyways var pathname = fullUrl.pathname[0] === '/' ? fullUrl.pathname : '/' + fullUrl.pathname; // Manually remove the leading question mark, if there is one var queryString = fullUrl.search[0] === '?' ? fullUrl.search.slice(1) : fullUrl.search; // Turn our params and headers into objects for easier reference var queryParameters = objMap(queryString, '&', '=', true); var headers = objMap(jqXhr.getAllResponseHeaders(), '\n', ':'); // Blindly push to the dataLayer because this fires within GTM dataLayer.push({ 'event': 'ajaxComplete', 'attributes': { // Return empty strings to prevent accidental inheritance of old data 'type': opts.type || '', 'url': fullUrl.href || '', 'queryParameters': queryParameters, 'pathname': pathname || '', 'hostname': fullUrl.hostname || '', 'protocol': fullUrl.protocol || '', 'fragment': fullUrl.hash || '', 'statusCode': jqXhr.status || '', 'statusText': jqXhr.statusText || '', 'headers': headers, 'timestamp': evt.timeStamp || '', 'contentType': opts.contentType || '', // Defer to jQuery's handling of the response 'response': (jqXhr.responseJSON || jqXhr.responseXML || jqXhr.responseText || '') } }); }); } function objMap(data, delim, spl, decode) { var obj = {}; // If one of our parameters is missing, return an empty object if (!data || !delim || !spl) { return {}; } var arr = data.split(delim); var i; if (arr) { for (i = 0; i < arr.length; i++) { // If the decode flag is present, URL decode the set var item = decode ? decodeURIComponent(arr[i]) : arr[i]; var pair = item.split(spl); var key = trim_(pair[0]); var value = trim_(pair[1]); if (key && value) { obj[key] = value; } } } return obj; } // Basic .trim() polyfill function trim_(str) { if (str) { return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); } } })(); </script>