网络 SDK - Google 标签管理器实施指南

文件

概述

信息:Web Attribution 是一项企业功能。请联系您的客户成功经理,为您的账户启用此功能。

本指南介绍如何使用Google Tag Manager(GTM) 实施 Singular Web SDK。此方法非常适合无法直接访问网站代码的团队或希望通过 GTM 管理跟踪的团队。

重要! 请勿在同一网站上同时使用 GTM 和本地 JavaScript 实现。只选择一种方法,以防止重复跟踪和夸大事件计数。Singular 不会自动重复事件。

  • 不要同时使用 Native JavaScript 和 Google Tag Manager 方法。只选择一种方法,以避免重复跟踪。
  • Singular Web SDK 设计用于在用户浏览器中运行客户端。它需要访问本地存储(localStorage 文档对象模型(DOM)等浏览器功能才能正常运行。请勿尝试在服务器端运行 SDK(例如,通过 Next.js SSR 或 node.js)--这将导致跟踪失败,因为服务器环境无法访问浏览器 API。

前提条件

在开始之前,请确保您拥有

实施步骤


第 1 步:将 Singular Web 跟踪模板添加到 GTM 容器中

  1. 登录Google Tag Manager账户,选择网站容器。
  2. 转到标签>新建
  3. 标签命名:"奇异初始标签
  4. 单击 "标签配置"框开始标签设置。
  5. 选择标签类型:并选择"在社区模板库中发现更多标签类型"
  6. 搜索"Singular "并选择"Singular Web Tracking"。点击"添加到工作区 "按钮。

第 2 步:初始化 SDK

  1. 在表单字段中填写以下内容:
    • 跟踪类型设为初始化
    • 在 Api Key 字段中输入实际的 SDK 密钥
    • 在 Secret 字段中输入实际的 SDK Secret
    • 输入您的实际产品 ID。它应该看起来像:com.website-name ,并且应该与 Singular 平台应用程序页面上的 BundleID 值相匹配。

      小贴士:使用特定的产品 ID 进行测试com.website-name.dev ,并在推送到生产环境之前进行更新。这样可以将所有测试数据与 Singular 报告中的生产应用程序分开。

    • 可选
      • 日志级别: SDK 调试日志到控制台的配置。默认为无。
      • 会话超时: 在 SDK 创建新会话之前,用户必须在多长时间内处于非活动状态。Singular 发送用户会话以计算用户保留时间,并启用重新参与属性。默认值为 30 分钟。
      • 跨子域跟踪
  2. 选择 "触发",配置一个触发器使该标签工作。
  3. 选择 "新建 "为 Tigger 命名:"奇异初始触发器"。
  4. 单击 "触发器配置"并选择"页面视图 - 窗口已加载",然后单击"保存"。
  5. 再次单击"保存"以保存标签。
  6. 在标签页面点击"预览",测试是否触发了奇异初始化标签。

    singular_init_tag_fired.png

成功!如果在 "预览 "控制台的 "已触发标签 "部分看到 "奇异初始化标签",则说明已成功配置初始化标签。

重要! 对于 SPA(单页面应用程序),每次路由到不同页面时都应触发页面访问跟踪类型。不要在加载的第一个页面上调用页面访问,因为初始化已经报告了页面访问。

解决方案概述

  • 使用自定义 JavaScript 变量来检测是否是首次加载页面。
  • 配置 2 个标签:
    • "单个初始化标签"(跟踪类型 = 初始化),仅在首次加载页面时触发。
    • "奇异页面访问标签"(跟踪类型 = 页面访问)使用历史更改触发器在每次路径更改(不包括初始加载)时触发。
  • 确保您的 SPA 将历史事件推送到路由更改的 dataLayer。

image5.png


第 3 步:跟踪事件

初始化 SDK 后,当用户在网站上执行重要操作时,您可以跟踪自定义事件。

重要!Singular 不会阻止重复事件!开发人员有责任添加保护措施,防止页面刷新或重复。建议专门针对收入事件采用一些重复数据删除方法,以防止错误的收入数据。有关示例,请参阅下面的 "步骤 5:防止重复事件"。

Basic EventConversion EventRevenue Event

基本事件跟踪

跟踪简单事件或使用有效的 JSON 添加自定义属性,以提供有关事件的更多上下文:

  1. 使用 Singular Web Tracking 模板为自定义事件创建新标签。

  2. 为事件标签命名。

  3. 选择跟踪类型 = 自定义事件

  4. 调整 "事件名称 "字段或设置适当的变量。

  5. 调整 "自定义用户 ID "字段或设置适当的变量。

  6. 如果需要在事件上传递键/值对,可调整 "属性"。

  7. 配置合适的触发器,以便仅在预期情况下触发标签。

  8. 保存触发器和标签,并在预览中进行测试。

custom_event.png

常见事件实现模式

Page Load EventsButton Click EventsForm Submission Events

为页面加载事件创建 GTM 触发器

要使用 Google 标签管理器实施 Singular Web SDK,需要创建一个页面加载触发器,在页面加载时触发。

快速设置:在 GTM 中,导航至" 触发器 新触发器配置 ",然后选择"页面视图 "作为触发器类型。对于大多数实施,选择 "所有页面视图 "可在每次页面加载时触发触发器。

有关完整的设置说明:请参阅 Google 标签管理器官方文档


步骤 4:设置客户用户 ID

您可以使用Singular SDK方法向Singular发送内部用户ID。

注意:如果您使用Singular的跨设备解决方案,您必须在所有平台上收集用户ID。

注意:如果多个用户使用一台设备,我们建议实施注销流程,为每次登录和注销设置取消设置用户 ID。

提示!使用与移动 SDK 相同的客户用户 ID。这样可以实现跨设备归因,并提供跨平台用户行为的完整视图。

只要用户在未登录的情况下在网站上执行操作,事件就会以 Singular 生成的用户 ID 发送到 Singular。但在用户注册或登录后,您可以将事件与网站上使用的用户 ID(如散列电子邮件地址)一起发送到 Singular。

Singular会在用户级数据导出(参见导出归因日志)以及内部商业智能回传(参见配置内部商业智能回传)中使用用户ID。

向 Singular 发送用户 ID 有两种方法:

  • 推荐: 如果知道网站打开时的用户 ID,请在初始化 SDK 时在初始化跟踪类型中设置用户 ID。这样,Singular 就能在第一次访问页面时获得用户 ID。
  • 或者,你也可以在任何时候触发一个跟踪类型为登录的标签,通常是在验证发生之后。注意:调用此标签不会触发事件。它只会设置用户 ID,并将其添加到今后的任何事件触发器中!

使用 Singular 设置用户 ID,请添加一个具有"登录 "跟踪类型的 Singular 标签:

  1. 在 Google 标签管理器账户中,单击标签 > 新建
  2. 在 "标签配置 "窗口中,单击 "标签配置",然后在 "标签类型"菜单中选择 "Singular Web Tracking"。
  3. 跟踪类型下,选择 "登录"。
  4. 在 "自定义用户 ID "下,输入包含用户 ID 的 Google 标签管理器变量。
  5. 单击 "触发 "并添加触发事件:用户登录或注册。
  6. 单击保存

image4.png

取消设置用户 ID,请添加 "注销 "跟踪类型的标签:

  1. 在 Google 标签管理器账户中,单击标签 > 新建
  2. 在 "标签配置 "窗口中,单击 "标签配置",然后在 "标签类型"菜单中选择 "奇异网络跟踪"。
  3. 跟踪类型下,选择 "注销"。
  4. 单击 "触发 "并添加触发事件:用户注销。
  5. 单击保存

image1.png

注释

  • 用户 ID 会一直存在,直到使用注销跟踪类型取消设置或用户删除本地存储
  • 关闭/刷新网站不会取消设置用户 ID。
  • 在隐身等私密模式下浏览将阻止 Singular 持久化用户 ID,因为本地存储会在关闭浏览器时自动删除。

步骤 5:防止重复事件

在谷歌标签管理器中防止重复事件

重要!这是最常见的 GTM 执行错误之一。如果没有适当的保护措施,当用户刷新页面、返回导航或重新触发操作时,Singular Web SDK 事件会多次触发,从而严重影响您的指标。

GTM 中为什么会出现重复事件

Google 标签管理器在每次页面加载时都会评估触发器,当用户刷新感谢页面、重新提交表单或使用浏览器的后退/前进按钮导航时,标签就会重新触发。出现这种情况是因为 GTM 缺乏对自定义事件的内置会话感知。


GTM 重复数据删除方法

会话存储方法:在浏览器的 sessionStorage 中存储唯一的事件标识符,以跟踪在用户会话期间哪些事件已经触发。

自定义 JavaScript 变量:创建变量,在允许触发新事件之前检查 localStorage 是否有之前触发的事件。

触发条件:为触发器添加条件逻辑,防止在出现重复指标时触发事件。


实施步骤

创建存储变量:创建自定义 JavaScript 变量,根据事件参数和用户会话为每个 Singular 事件生成唯一标识符。

创建检查变量:创建另一个自定义 JavaScript 变量,用于检查当前事件标识符是否已存在于浏览器存储中。

添加触发条件:修改 Singular Web SDK 触发器,加入 "重复检查 "变量等于 "false "的条件。

触发后存储:在 Singular 事件成功触发后,使用 GTM 的标签序列功能触发自定义 HTML 标签,将事件标识符存储到 sessionStorage 中。


GTM 特定注意事项

标签排序:使用 "高级设置 "的 "标签排序 "功能,确保在主 Singular 标签完成后启动存储标签。

变量评估:请记住,自定义 JavaScript 变量在每次引用时都要进行评估,因此要对性能进行优化。

跨域限制:SessionStorage 是针对特定域的,因此如有需要,可采用基于 cookie 的解决方案进行跨域跟踪。

详细实施:请参考全面的 GTM 重复数据删除指南:


第 6 步:测试您的 GTM 实施

  1. 打开GTM 预览模式并加载您的网站。
  2. 检查
  • SDK 加载(网络请求singular-sdk.js)
  • 事件按预期触发,且每个操作只触发一次
  • 浏览器控制台中没有与singular相关的错误
  • 网络请求被发送到sdk-api-v1.singular.net
  1. 使用浏览器开发工具中的 "网络"选项卡验证是否发送了正确的有效载荷

成功!如果您在网络请求中看到正确的 Singular 事件,并且没有重复,那么您就可以上线了!


第 7 步:实施网络到应用程序转发

网络到应用程序转发

使用 Singular Web SDK 跟踪从网站到移动应用的用户旅程,从而将准确的网络营销活动归因于移动应用的安装和重新吸引。 按照以下步骤设置网络到应用转发,包括为桌面用户提供 QR 码支持。

  1. 按照我们的《网站到移动应用归因转发指南》,为移动网络归因配置 Singular Web SDK。
  2. 用于桌面网络到应用程序跟踪:
    • 创建二维码生成器清理标签
      1. 在 GTM 中导航到 "标签">"新建",然后选择 "自定义 HTML"。
      2. 添加代码
        • 注入您选择的 QRCode 库
        • window.singularSdk.buildWebToAppLink(baselink);获取网络到应用程序链接
        • 从返回的链接生成 QRCode。
        • 更新页面上的 QRCode 图像。
      3. 为标签命名:"Singular - QR 码生成器(清理
      4. 无需触发器:清理标签从主标签继承触发器
      5. 配置 Singular Init 标签
        • 点击标签配置
        • 转到高级设置 > 标记排序
        • 选中 "在[奇异初始标签]触发后触发一个清理标签"。
        • 从下拉菜单中选择二维码生成器标签
        • 保存标签,并在预览模式下进行测试。

提示!移动应用内浏览器网页视图(如 Facebook、Instagram 和 TikTok 使用的视图)会导致用户移动到设备的本机浏览器时改变 Singular 设备 ID,从而扰乱归因。

为避免这种情况,请始终为每个广告网络使用正确的 Singular 跟踪链接格式:


高级主题

添加全局属性

全局属性
#

Singular SDK可以让你定义自定义属性,这些属性将与应用程序发送的每个会话和事件一起发送到Singular服务器。这些属性可以代表关于用户、应用程序模式/状态或其他任何信息。

  • 您最多可以将 5 个全局属性定义为有效的 JSON 对象。全局属性会被持久保存在浏览器localstorage 中,直到被清除或浏览器上下文发生变化。

  • 每个属性名称和值的长度不超过 200 个字符。如果传递的属性名称或值较长,则会被截断为 200 个字符。

  • 全局属性目前反映在 Singular 的用户级事件日志(请参阅导出属性日志)和回传中。

  • 如果需要,全局属性可以在从 Singular 发送到第三方的回传中进行匹配。

Google 标签管理器初始化标签目前不支持在 SDK 初始化前设置全局属性。 不过,要在初始化后处理全局属性,必须创建自定义 HTML 标签并执行本地 JavaScript 函数:setGlobalProperties(),getGlobalProperties(),clearGlobalProperties()

创建用于处理全局属性的自定义 HTML 标签

  1. 在 Google 标签管理器界面,单击左侧导航菜单中的标签,然后单击新建按钮创建新标签。

  2. 单击标签配置,然后从可用标签类型列表中选择自定义 HTML。

  3. 在 HTML 字段中,粘贴全局属性方法代码。 参见下面的选项:

  4. 单击触发并为标签指定一个触发选项。

  5. 在顶部的标签名称字段中给标签起一个描述性的名称,如 "奇异--设置全局属性"。

设置全局属性后,全局属性将在所有事件中持续存在,直至取消或清除。

单击 "预览 "测试您的实现。在浏览器控制台中验证全局属性是否已设置。

高级配置请参阅 Google 官方的自定义 HTML 文档

Setting Global PropertiesGetting GlobalPropertiesClearing Global Properties

用于设置全局属性的自定义 HTML 标签代码

<script>
/**
 * Set a Singular global property before SDK initialization.
 * Allows up to 5 key/value pairs. Optionally overwrites existing value for a key.
 * 
 * @param {string} propertyKey - The property key to set.
 * @param {string} propertyValue - The property value to set.
 * @param {boolean} overrideExisting - Whether to overwrite the property if it already exists.
 */

// Example usage - customize these values for your implementation
window.singularSdk.setGlobalProperties('user_type', 'premium', true);
window.singularSdk.setGlobalProperties('app_version', '2.1.0', false);
</script>

有机搜索跟踪

有机搜索跟踪示例
#

创建有机搜索跟踪设置标签

至关重要!- 此标签必须在 Singular SDK 初始化之前触发

此自定义 HTML 标签修改文档 URL,以添加 Singular Web SDK 在初始化过程中需要读取的有机搜索跟踪参数(wpsrc 和 wpcn)。为确保正确的执行顺序,标签排序至关重要。

本示例是启用有机搜索跟踪的变通方法。代码只能作为示例使用,由网站开发人员根据营销部门的需求进行更新和维护。 有机搜索跟踪可能因广告商而异。 请查看示例,并根据您的需求进行调整。

为何使用?

  • 确保正确跟踪有机搜索访问,即使没有广告系列参数。

  • 将Singular "来源 "参数wpsrc与(referrer)值和 "广告系列名称 "参数wpcn 作为 "OrganicSearch "附加到 URL,以明确归属。

  • 将当前 URL 和推荐人存储在localStorage中,以便日后使用。

  • 纯 JavaScript,零依赖性,易于集成。

工作原理

  1. 检查页面 URL 是否有来自(Google、Facebook、TikTok、UTM 等)的已知营销活动参数。

  2. 如果不存在营销活动参数,且推荐人是搜索引擎,则附加:

    • wpsrc (以推荐人作为其值
    • wpcn (以 OrganicSearch 作为其值)
  3. 更新浏览器中的 URL,无需重新加载页面。

  4. localStorage中的当前 URL 和推荐人存储为sng_urlsng_ref

使用方法

  1. 在 GTM 中导航到 "标签">"新建",然后单击 "标签配置",再选择 "自定义 HTML"。
  2. 如下所示粘贴完整的 JavaScript 代码。
  3. 跳过此标签的触发器创建。由于此标签必须在奇异初始化标签之前运行,我们将使用标签排序功能将其配置为在初始化之前触发。

    1. 使用一个描述性的名称,如 "Singular - 有机搜索设置"。
    2. 打开 Singular Web SDK 初始化标签。
    3. 点击标签配置
    4. 转到高级设置 > 标签排序。
    5. 选中 "在[Singular Init Tag]触发前触发一个设置标签"。
    6. 从下拉菜单中选择 "Singular - 有机搜索设置 "标签。
    7. 建议选中 "如果[设置标签]失败,则不触发[奇异初始化标签]",以防止初始化时 URL 修改不完整。
    8. organic_search.png
    9. 保存标签。
  4. 使用 GTM 的预览模式进行验证:

    • 设置标签首先触发并修改 URL。
    • Singular Init 标签第二个触发并读取修改后的 URL 参数。
    • 标签之间没有时间冲突。

对于高级标签排序:请参阅 Google 官方文档:


Organic Search Tracking Code
<script>
(function() {
    // singular-web-organic-search-tracking: setupOrganicSearchTracking.js
    // Tracks organic search referrals by appending wpsrc and wpcn to the URL if no campaign parameters exist and the referrer is a search engine.

    // Configuration for debugging (set to true to enable logs)
    var debug = true;

    // List of campaign parameters to check for exclusion
    var campaignParams = [
        'gclid', 'fbclid', 'ttclid', 'msclkid', 'twclid', 'li_fat_id',
        'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'wpsrc'
    ];

    // Whitelist of legitimate search engine domains (prevents false positives)
    var legitimateSearchEngines = new Set([
        // Google domains
        'google.com', 'google.co.uk', 'google.ca', 'google.com.au', 'google.de', 
        'google.fr', 'google.it', 'google.es', 'google.co.jp', 'google.co.kr',
        'google.com.br', 'google.com.mx', 'google.co.in', 'google.ru', 'google.com.sg',
        
        // Bing domains  
        'bing.com', 'bing.co.uk', 'bing.ca', 'bing.com.au', 'bing.de',
        
        // Yahoo domains
        'yahoo.com', 'yahoo.co.uk', 'yahoo.ca', 'yahoo.com.au', 'yahoo.de',
        'yahoo.fr', 'yahoo.it', 'yahoo.es', 'yahoo.co.jp',
        
        // Other search engines
        'baidu.com', 'duckduckgo.com', 'yandex.com', 'yandex.ru',
        'ask.com', 'aol.com', 'ecosia.org', 'startpage.com', 
        'qwant.com', 'seznam.cz', 'naver.com', 'daum.net'
    ]);

    // Extract main domain from hostname (removes subdomains)
    function getMainDomain(hostname) {
        if (!hostname) return '';
        
        var lowerHost = hostname.toLowerCase();
        
        // Handle special cases for known search engines with country codes
        var searchEnginePatterns = {
            'google': function(host) {
                // Match google.TLD patterns more precisely
                if (host.indexOf('google.co.') !== -1 || host.indexOf('google.com') !== -1) {
                    var parts = host.split('.');
                    for (var i = 0; i < parts.length - 1; i++) {
                        if (parts[i] === 'google') {
                            return parts.slice(i).join('.');
                        }
                    }
                }
                return null;
            },
            'bing': function(host) {
                if (host.indexOf('bing.co') !== -1 || host.indexOf('bing.com') !== -1) {
                    var parts = host.split('.');
                    for (var i = 0; i < parts.length - 1; i++) {
                        if (parts[i] === 'bing') {
                            return parts.slice(i).join('.');
                        }
                    }
                }
                return null;
            },
            'yahoo': function(host) {
                if (host.indexOf('yahoo.co') !== -1 || host.indexOf('yahoo.com') !== -1) {
                    var parts = host.split('.');
                    for (var i = 0; i < parts.length - 1; i++) {
                        if (parts[i] === 'yahoo') {
                            return parts.slice(i).join('.');
                        }
                    }
                }
                return null;
            }
        };
        
        // Try specific patterns for major search engines
        for (var engine in searchEnginePatterns) {
            if (lowerHost.indexOf(engine) !== -1) {
                var result = searchEnginePatterns[engine](lowerHost);
                if (result) return result;
            }
        }
        
        // Handle other known engines with simple mapping
        var otherEngines = {
            'baidu.com': 'baidu.com',
            'duckduckgo.com': 'duckduckgo.com', 
            'yandex.ru': 'yandex.ru',
            'yandex.com': 'yandex.com',
            'ask.com': 'ask.com',
            'aol.com': 'aol.com',
            'ecosia.org': 'ecosia.org',
            'startpage.com': 'startpage.com',
            'qwant.com': 'qwant.com',
            'seznam.cz': 'seznam.cz',
            'naver.com': 'naver.com',
            'daum.net': 'daum.net'
        };
        
        for (var domain in otherEngines) {
            if (lowerHost.indexOf(domain) !== -1) {
                return otherEngines[domain];
            }
        }
        
        // Fallback: Extract main domain by taking last 2 parts (for unknown domains)
        var parts = hostname.split('.');
        if (parts.length >= 2) {
            return parts[parts.length - 2] + '.' + parts[parts.length - 1];
        }
        
        return hostname;
    }

    // Get query parameter by name, using URL.searchParams with regex fallback for IE11
    function getParameterByName(name, url) {
        if (!url) url = window.location.href;
        try {
            return new URL(url).searchParams.get(name) || null;
        } catch (e) {
            if (debug) console.warn('URL API not supported, falling back to regex:', e);
            name = name.replace(/[\[\]]/g, '\\$&');
            var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)');
            var results = regex.exec(url);
            if (!results) return null;
            if (!results[2]) return '';
            return decodeURIComponent(results[2].replace(/\+/g, ' '));
        }
    }

    // Check if any campaign parameters exist in the URL
    function hasAnyParameter(url, params) {
        for (var i = 0; i < params.length; i++) {
            if (getParameterByName(params[i], url) !== null) {
                return true;
            }
        }
        return false;
    }

    // Improved search engine detection - only checks hostname, uses whitelist
    function isSearchEngineReferrer(referrer) {
        if (!referrer) return false;
        
        var hostname = '';
        try {
            hostname = new URL(referrer).hostname.toLowerCase();
        } catch (e) {
            // Fallback regex for hostname extraction (IE11 compatibility)
            var match = referrer.match(/^(?:https?:\/\/)?([^\/\?#]+)/i);
            hostname = match ? match[1].toLowerCase() : '';
        }
        
        if (!hostname) return false;
        
        // First check: exact match against whitelist
        if (legitimateSearchEngines.has(hostname)) {
            if (debug) console.log('Exact match found for:', hostname);
            return true;
        }
        
        // Second check: subdomain of legitimate search engine
        var hostParts = hostname.split('.');
        if (hostParts.length >= 3) {
            // Try domain.tld combination (e.g., google.com from www.google.com)
            var mainDomain = hostParts[hostParts.length - 2] + '.' + hostParts[hostParts.length - 1];
            if (legitimateSearchEngines.has(mainDomain)) {
                if (debug) console.log('Subdomain match found for:', hostname, '-> main domain:', mainDomain);
                return true;
            }
            
            // Try last 3 parts for country codes (e.g., google.co.uk from www.google.co.uk)
            if (hostParts.length >= 3) {
                var ccDomain = hostParts[hostParts.length - 3] + '.' + hostParts[hostParts.length - 2] + '.' + hostParts[hostParts.length - 1];
                if (legitimateSearchEngines.has(ccDomain)) {
                    if (debug) console.log('Country code domain match found for:', hostname, '-> cc domain:', ccDomain);
                    return true;
                }
            }
        }
        
        if (debug) {
            console.log('Hostname not recognized as legitimate search engine:', hostname);
        }
        
        return false;
    }

    // Main function to update URL with organic search tracking parameters
    function setupOrganicSearchTracking() {
        var url = window.location.href;
        var referrer = document.referrer || '';

        // Store URL and referrer in localStorage
        try {
            localStorage.setItem('sng_url', url);
            localStorage.setItem('sng_ref', referrer);
        } catch (e) {
            if (debug) console.warn('localStorage not available:', e);
        }

        if (debug) {
            console.log('Current URL:', url);
            console.log('Referrer:', referrer);
        }

        // Skip if campaign parameters exist or referrer is not a search engine
        var hasCampaignParams = hasAnyParameter(url, campaignParams);
        if (hasCampaignParams || !isSearchEngineReferrer(referrer)) {
            if (debug) console.log('Skipping URL update: Campaign params exist or referrer is not a legitimate search engine');
            return;
        }

        // Extract and validate referrer hostname
        var referrerHostname = '';
        try {
            referrerHostname = new URL(referrer).hostname;
        } catch (e) {
            if (debug) console.warn('Invalid referrer URL, falling back to regex:', e);
            var match = referrer.match(/^(?:https?:\/\/)?([^\/]+)/i);
            referrerHostname = match ? match[1] : '';
        }

        // Extract main domain from hostname
        var mainDomain = getMainDomain(referrerHostname);
        
        if (debug) {
            console.log('Full hostname:', referrerHostname);
            console.log('Main domain:', mainDomain);
        }

        // Only proceed if main domain is valid and contains safe characters
        if (!mainDomain || !/^[a-zA-Z0-9.-]+$/.test(mainDomain)) {
            if (debug) console.log('Skipping URL update: Invalid or unsafe main domain');
            return;
        }

        // Update URL with wpsrc and wpcn parameters
        var urlObj;
        try {
            urlObj = new URL(url);
        } catch (e) {
            if (debug) console.warn('URL API not supported, cannot modify URL:', e);
            return;
        }
        
        // Set wpsrc to the main domain (e.g., google.com instead of tagassistant.google.com)
        urlObj.searchParams.set('wpsrc', mainDomain);
        
        // Set wpcn to 'Organic Search' to identify the campaign type
        urlObj.searchParams.set('wpcn', 'Organic Search');

        // Update the URL without reloading (check if history API is available)
        if (window.history && window.history.replaceState) {
            try {
                window.history.replaceState({}, '', urlObj.toString());
                if (debug) console.log('Updated URL with organic search tracking:', urlObj.toString());
            } catch (e) {
                if (debug) console.warn('Failed to update URL:', e);
            }
        } else {
            if (debug) console.warn('History API not supported, cannot update URL');
        }
    }

    // Execute the function
    setupOrganicSearchTracking();
})();
</script>

跨子域跟踪

默认情况下,Singular 网站 SDK 会生成一个 Singular 设备 ID,并使用浏览器存储将其保存。由于该存储无法在子域之间共享,SDK 最终会为每个子域生成一个新 ID。

如果要跨子域持久化 Singular 设备 ID,可以使用以下选项之一:

方法 B(高级):手动设置奇异设备 ID
#

方法 B(高级):手动设置单个设备 ID

如果不想让Singular SDK自动持久化设备ID,可以跨域手动持久化ID,例如使用顶级域cookie或服务器端cookie。ID值应该是Singular之前生成的有效uuid4格式的ID。

注:在调用 Init track-type 标签后,定义一个自定义 JavaScript 变量并调用 singularSdk.getSingularDeviceId(),即可读取 Singular 设备 ID。

mceclip2.png


常见的 GTM 实施问题

所需的设备标识符
事件触发多次 完善或限制触发器,使用 "每页一次 "或添加条件
产品 ID 格式不正确 产品 ID 必须使用反向 DNS 符号 (com.company.site)
未跟踪事件 检查事件名称的拼写和大小写;确保在事件标签触发前加载singularSDK
SDK 脚本被阻止 广告拦截器或限制性内容安全策略;如果问题持续存在,考虑转用本地 JS 实现

最佳实践

  • 保持 GTM 井井有条:明确命名标签和触发器,并记录其用途。
  • 定期审核未使用或遗留的标记。
  • 尽量减少触发器的数量,以降低重复事件的风险。
  • 更改后,一定要在 GTM 的预览模式中进行全面测试,然后再推送上线。
  • 如果您使用基于 cookie 的跟踪(跨子域跟踪),请相应更新您的隐私政策

相关文章