透過 Google Tag Manager 追蹤 AJAX 表單事件

透過 Google Tag Manager 追蹤 AJAX 表單事件

大家好,我是 Eric。

日前在幫客戶設定轉換追蹤時,發現客戶的購物系統,使用了名為 AJAX 的技術來跟伺服器傳遞資料。換句話說,從開始結帳到完成購買,網址都沒有改變過,因此無法用感謝頁的網址作為完成轉換的觸發條件。

Analytics mania 針對這種情況,提供 GTM 偵測 AJAX 事件的解決方法。今天這篇文章將會跟大家說明為什麼需要追蹤 AJAX、如何設定你的 GTM來偵測 AJAX 表單的事件。

這篇文章適合已經具備 GTM 使用經驗,並且知道如何存取資料層變數的人閱讀。如果你希望能夠更清楚了解 Analytics mania 實作原理,那你會需要對於 JavaScript 與 AJAX 有基本理解。

為什麼要追蹤 AJAX

request and response in 透過 Google Tag Manager 追蹤 AJAX 表單事件

結論來說,是因為現在的網站大量使用 AJAX 的技術。

你有遇過送出表單時,頁面就會重新整理的情況嗎?這是因為我們在送出表單時,我們便送出一次 HTTP 要求 (HTTP request) 給伺服器,而伺服器再回覆一個回應 (response) 給你的瀏覽器 (譬如 Safari、Google Chrome)。

這樣講可能有點難以想像,我們再舉另一個例子。

試著想像一下,當你使用 Google Docs 撰寫文章時,你想要儲存目前的進度。這時,如果是傳統的做法,你需要按下儲存按鈕,接著整個網頁會重新整理一次。

如果你想要有「自動儲存」這種很方便的功能呢?那麼每次 Google 準備自動儲存時,會在你打字打到一半時便重新整理頁面。

為了要改善這種被中斷的使用體驗,許多網站開始使用了 AJAX 技術。

當你的瀏覽器準備送出 HTTP 要求時,是透過 JavaScript,將資料動態的整理好,並在背景傳送資料。而當伺服器回傳回應時,則透過 JavaScript 將取得的資料以動態的方式,呈現在你當前的頁面中。

這種作法很廣泛的用在聯絡表單 (如 applemint 使用的表單)、網路商城與其他網頁程式 (Web App) 中,因此有越來越多需要追蹤 AJAX 事件的情境。

如何用 Google Tag Manager 追蹤 AJAX

新增自訂 HTML 利用 jQuery 的 ajaxComplete 偵測 AJAX 事件

新增自訂 HTML 利用 jQuery 的 ajaxComplete 偵測 AJAX 事件

首先,先在 Google Tag Manager 中新增 [自訂 HTML],複製貼上下列的程式碼片段。注意,這個做法必須搭配 jQuery 使用。

<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, '');
    }
}
})();

 /*
  * v0.1.0
  * Created by the Google Analytics consultants at http://www.lunametrics.com
  * Written by @notdanwilkerson
  * Documentation: http://www.lunametrics.com/blog/2015/08/27/ajax-event-listener-google-tag-manager/
  * Licensed under the Creative Commons 4.0 Attribution Public License
  */
</script>

加入這個自訂 HTML 後,你的網頁就會在完成 AJAX 事件時,推送一個名為 ‘ajaxComplete’資料層事件。

檢視 ajaxComplete 的資料層變數

開啟 [預覽模式],查看你的 AJAX 事件

接著開啟 [預覽模式],查看你的 AJAX 事件。

在完成 AJAX 的事件時,你會看到左側的事件列表中,多了一個 ajaxComplete 事件。點擊該事件後,點擊預覽視窗的 Data Layer,來檢視這個 AJAX 事件的屬性。

以截圖為例,我在進行測試訂單時,注意到每次傳送要求的網址都會改變,因此我便會以 attributes.url 來作為判斷標準。

新增變數與觸發條件

新增資料層變數

接著到 [變數],新增一個名為 DLV – AJAX Attributes 的變數,變數類型選擇 [資料層變數],這裡以 attributes.url 作為變數值。

設定完變數後,便能進一步以這個變數作為觸發條件。

新增觸發條件

新增一個名為 AJAX – shopping_cart_result 的觸發條件,選擇自訂事件,事件名稱為 ajaxComplete。

接著選擇 [部分的自訂事件],選擇當 DLV – AJAX Attributes 包含 shopping_cart_result。

到這裡,便完成了 AJAX 事件追蹤的前置作業,可以利用這個觸發條件來安裝轉換代碼,如 Google Ads 或 Facebook Ads 的轉換代碼。

結語

目前世界上有 40% 的網站是透過 WordPress 製作,很多時候可以利用 WordPress 的外掛來輔助 GTM 的設定,譬如 Google Tag Manager for WordPress。

然而,每個網站都是獨一無二的,因此針對不同的情境,都還是需要多利用 GTM 的預覽模式來找出適合該網站的解決方式。

如果你的網站也有任何需要埋設追蹤碼的需求,或是自行設定追蹤碼時,遇到任何問題,都可以透過下方的連結與 applemint 聯絡。

從這裡聯絡 applemint!

Eric Chuang

相關文章

與我們聯繫