更新時間:2024年09月05日
如果你的網站使用Vimeo嵌入產品介紹、品牌影片、案例分享或線上課程,你可能會想知道:
- 有多少人實際播放影片?
- 使用者看到影片的哪個進度後離開?
- 哪些影片最受歡迎?
- 看過影片的使用者是否更容易轉換?
然而,與YouTube不同的是,GTM內建的影片觸發條件只支援YouTube,無法直接追蹤Vimeo。
因此,如果要將Vimeo的觀看資料傳送到GA4,就需要額外透過JavaScript監聽Vimeo播放器事件。
Vimeo追蹤的運作原理
由於GTM不支援Vimeo,因此需要自行建立監聽機制。
整體流程如下:
- 偵測頁面是否存在Vimeo影片
- 監聽影片播放事件
- 將事件推送到Data Layer
- GTM接收事件
- GA4接收事件與參數
Vimeo追蹤範例
假設我現在要追蹤 https://grounded.world/our-work-hidden-camera-on-hunger/上的Vimeo影片播放:

設定過程
添加Vimeo影片監聽代碼
Step 1:建立 Vimeo偵測變數
首先建立一個自訂JavaScript變數,用於判斷頁面是否存在 Vimeo iframe,避免監聽程式在所有頁面執行
在GTM中點擊「變數」——「新增」——「請選擇變數類型以開始設定…」——「自訂 JavaScript」,命名為 “Custom Javascript – Is Vimeo Present”, 然後做如下設定:

//this function checks for the presence of an embedded Vimeo video
//if one or more videos are present, return true, otherwise return false
function () {
for (var e = document.getElementsByTagName("iframe"), x=0; x < e.length; x++) {
if (/^https?:\/\/player.vimeo.com/.test(e[x].src)) {
return true;
}
}
return false;
}
Step 2:設定觸發條件
在GTM中點擊「觸發條件」—「新增」—「選擇一個觸發條件類型以開始設定」——「網頁瀏覽 – 視窗已載入」,命名為“Pageview – Vimeo Player Is Present”,然後做如下設定:

這樣只有包含Vimeo影片的頁面才會執行監聽程式。
Step 3:建立Vimeo Listener Tag
接下來新增一個 Custom HTML Tag。
在GTM中點擊「代碼」—「新增」—「請選擇代碼類型以開始設定…」——「自訂HTML」,命名為“cHTML – Vimeo Listener”,然後做如下設定:

此程式負責監聽播放、監聽暫停、監聽觀看進度,並透過 dataLayer.push() 傳送事件
<!--
Google Analytics Tag Manager (V2) custom HTML tag for Vimeo video tracking
Copyright 2016, Cardinal Path, Inc.
Original author: Bill Tripple <[email protected]>
Revised by: Bogdan Bistriceanu <[email protected]>
Updated by: Julius Fedorovicius <[email protected]> and Richard Outram <[email protected]>
Version 2.1
--><script>
var dataLayer = (typeof(dataLayer) !== "undefined" && dataLayer instanceof Array) ? dataLayer : [];
var videoLabels=[];
var lastP=[];
//we declare variables that will hold information about the video being played
var _playerTitle = {}, _playerAuthor = {}, _playerAuthorURL = {}, _playerUploadDate = {};
try{
init();
}
catch(err){
dataLayer.push({
'event': 'gtm.error',
'errorMessage': e.message,
'tag': 'CP - UA - Vimeo Video Listener'
})
}
function init(){
try{
var player=document.getElementsByTagName("iframe");
for (i = 0; i < player.length; ++i) {
var url=player[i].getAttribute("src");
if(/player\.vimeo\.com\/video/.test(url)){ // vimeo iframe found
if(!player[i].hasAttribute("id")){ // id attribute missing
player[i].setAttribute("id","vimeo_id_"+i); // add id attribute
}
var urlUpdated=false;
if(!/api=/.test(url)){ // check to see if api parameter is in src attribute
url=updateUrl(url,"api",1);
urlUpdated=true;
}
if(!/player_id=/.test(url)){ // check if player_id is in src attribute
url=updateUrl(url,"player_id",player[i].getAttribute("id"));
urlUpdated=true;
}
if(urlUpdated){ // repopulate src attribute with added parameters
player[i].setAttribute("src",url)
}
videoLabels[player[i].getAttribute("id")]=player[i].getAttribute("src"); // id to label dictionary
}
}
// Listen for messages from the player
if (window.addEventListener){
window.addEventListener('message', onMessageReceived, false);
}
else {
window.attachEvent('onmessage', onMessageReceived, false);
}
}
catch(err){
}
}
function updateUrl(url,param,value){
try{
return url+((/\?/.test(url)) ? "&" : "?")+param+"="+value;
}
catch(err){
}
}
// Handle messages received from the player
function onMessageReceived(e) {
try{
var data = e.data;
if(typeof data === "string"){
data = JSON.parse(data);
}
switch (data.event) {
case 'ready':
onReady(data);
break;
case 'play':
onPlay(data);
break;
case 'pause':
onPause(data);
break;
case 'timeupdate':
onPlayProgress(data);
break;
}
}
catch(err){
}
}
// Helper function for sending a message to the player
function post(action, value) {
try{
var data = {
method: action
};
if (value) {
data.value = value;
}
var message = JSON.stringify(data);
var player = document.getElementsByTagName("iframe");
var url;
var prot;
for (i = 0; i < player.length; ++i) {
url=player[i].getAttribute("src");
if(/player\.vimeo\.com\/video/.test(url)){
// Check if protocol exists
prot = player[i].getAttribute('src').split('?')[0].split('//')[0];
// If protocol doesn't exist, then need to append to "url"
if (!prot){
url="https:" + player[i].getAttribute("src").split('?')[0];
}
player[i].contentWindow.postMessage(data, url);
}
}
}
catch(err){
}
}
function getLabel(id){
try{
return videoLabels[id].split('?')[0].split('/').pop();
}
catch(err){
}
}
//our function that will use the Vimeo oEmbed API to retrieve additional information about the video
function getVimeoInfo(url, callback) {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
document.getElementsByTagName('body')[0].appendChild(script);
}
//the callback function which takes the data received from the Vimeo oEmbed API and places it into the corresponding objectes
function vimeoCallback(e){
//console.log(e);
_playerTitle[e['video_id']] = e['title'];
_playerAuthor[e['video_id']] = e['author_name']
_playerAuthorURL[e['video_id']] = e['author_url']
_playerUploadDate[e['video_id']] = e['upload_date']
}
function onReady(data) {
try{
//execute our function which queries the Vimeo oEmbed API once the embedded videos are "ready"
getVimeoInfo("https://www.vimeo.com/api/oembed.json?url=https://vimeo.com/"+getLabel(data.player_id)+"&callback=vimeoCallback", vimeoCallback);
post('addEventListener', 'playProgress');
}
catch(err){
}
}
// Track progress: 25%, 50%, 75%, 100%
function onPlayProgress(data) {
try{
var t = data.data.duration - data.data.seconds <= 1.5 ? 1 : (Math.floor(data.data.seconds / data.data.duration * 20) / 20).toFixed(2); if (!lastP[data.player_id] || t > lastP[data.player_id]) {
lastP[data.player_id]=t;
if (parseFloat(t) != 0 && (t==0.15 || t==0.5 || t===0.9)){
dataLayer.push({
event: "video",
video_percentage: t*100+ "% Complete",
video_name: _playerTitle[getLabel(data.player_id)].toLowerCase() + " - " + getLabel(data.player_id),
vimeo_playerID: getLabel(data.player_id),
vimeo_playerTitle: _playerTitle[getLabel(data.player_id)].toLowerCase(),
vimeo_playerAuthor: _playerAuthor[getLabel(data.player_id)].toLowerCase(),
vimeo_playerAuthorURL: _playerAuthorURL[getLabel(data.player_id)].toLowerCase(),
vimeo_playerUploadDate: _playerUploadDate[getLabel(data.player_id)],
nonInteractive: true
})
}
}
}
catch(err){
}
}
</script>
設定Viemo事件
Step 1:建立 Data Layer Variables
Vimeo Listener會推送以下資料:video_name、video_percentage,因此需要建立兩個 Data Layer Variable。
在GTM中點擊「變數」——「新增」——「請選擇變數類型以開始設定…」——「資料層變數」,命名為 “Video Name”, 然後做如下設定:

同理設定Video Percentage。
Step 2:建立自訂事件觸發器
Listener推送的事件名稱為video。
在GTM中點擊「觸發條件」—「新增」—「選擇一個觸發條件類型以開始設定」——「自訂事件」,命名為“Custom – Video Interaction”,然後做如下設定:

Step 3:建立 GA4 Event Tag
最後就是設定代碼。
在GTM中點擊「代碼」—「新增」—「請選擇代碼類型以開始設定…」——「Google Analytics(分析):GA4 事件」,命名為“Event—Video Tracking”,做如下設定:

測試設定是否成功
GTM裡預覽測試是否成功,測試沒問題才發佈。
在GA4註冊自訂定義
即使事件已經送達 GA4,也不代表可以直接在報表中使用,還需要在自訂定義裡新增事件層級的自訂維度:
| 維度名稱 |
事件參數 |
| Video Name |
video_name |
| Video Percentage |
video_percentage |
完成後約需 24~48 小時才會出現在標準報表中。
常見問題
為什麼GTM的影片觸發條件抓不到Vimeo?
因為GTM內建影片追蹤只支援YouTube,不支援Vimeo。
可以追蹤影片播放百分比嗎?
可以。
常見做法是追蹤:25%、50%、75%、100%,藉此分析使用者的影片觀看完成率。