Video tracking for Youtube videos can easily be configured with build-in Google Tag Manager triggers. But other video players are not currently supported. Fortunatly most of the popular video players have JavaScript API which allows to create listeners and push data about video player events to dataLayer.

JWPlayer Tracking

JWPlayer has very detailed API reference here https://developer.jwplayer.com/jw-player/docs/developer-guide/api/javascript_api_introduction/

Using JWPlayer API it is possible to track a number of different events: play, pause, volume change, resize, advertising interactions, etc.

Here is a sample script that can be either implemented to the web page or added as custom tag in Google Tag Manager to push data about some of these events to dataLayer.

(function(dataLayer){
  var i = 0;
  var markers = [10,25,50,75,90]; //adjust these values if you want different progress reports
  var playersMarkers = [];

  function findObjectIndexById(haystack, key, needle) {
    for (var i = 0; i < haystack.length; i++) {
      if (haystack[i][key] == needle) {
        return i;
      }
    }
    return null;
  }
  //Function call to push to dataLayer, passing in current scope of this, 
  // the eventType value and the eventInteraction value for the DL push
  function eventToDataLayer (thisObject, eventType, eventInteraction) {
    var eventName;
    if (thisObject.getPlaylistItem().title) {
      eventName = thisObject.getPlaylistItem().title;
    } else {
      eventName = 'not set';
    }
  
    dataLayer.push({
      event: "video", 
      eventCategory: "JW Player",
      eventAction: eventType,
      eventLabel: eventName
    });
  }

//loops through all JWPlayers on the page
  while (window.jwplayer(i).id) {
    var player = window.jwplayer(i++);

    //Pushes an object of player.id and progress markers to the array playersMarkers
    playersMarkers.push({
      'id': player.id,
      'markers': []
    });
    
 
    player.on('setupError',
      function(e) {
        eventToDataLayer (this, 'Video error', e.message); 
      }
    );

    player.on('play',
          function(e){
            var playResume ;
            if (this.getPosition() < 2) {
              playResume = 'Played video';
            } else {
              playResume = 'Resumed video';
            }
            eventToDataLayer (this, 'Played video', playResume);
          }
        );

    player.on('pause',
      function(e){
        eventToDataLayer (this, 'Paused video', 'Paused video');
      }
    );

    player.on('complete',
      function(e){
        eventToDataLayer (this, '100%', '100%');
      }
    );

    player.on('time',
      function(e){
        var percentPlayed = Math.floor(e.position*100/e.duration);
        var playerMarkerIndex = findObjectIndexById(playersMarkers,'id',this.id); 
        if(markers.indexOf(percentPlayed)>-1 && playersMarkers[playerMarkerIndex].markers.indexOf(percentPlayed)==-1)
          {
          playersMarkers[playerMarkerIndex].markers.push(percentPlayed);
          eventToDataLayer (this, percentPlayed + '%', percentPlayed + '%');
        }
      }
      );

    player.on('error',
      function(e){
        eventToDataLayer (this, 'Video error', e.message); 
      }
    );
    
  }
})(window.dataLayer = window.dataLayer || []);

Vimeo Tracking

Another popular video plauer is Vimeo. Here is the documentation on the JavaScript API for Vimeo player https://github.com/vimeo/player.js.

Here is a sample tracking code that uses the Vimeo JavaScript API.

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 'playProgress':
                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', 'play');
        post('addEventListener', 'pause');
        post('addEventListener', 'finish');
        post('addEventListener', 'playProgress');
    }
    catch(err){
    }
}

function onPlay(data){
    try{
        dataLayer.push({
            event: "vimeo",
            eventCategory: "vimeo",
            eventAction: "vimeo play",
            eventLabel: _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){
    }
}

function onPause(data){
    try{
        dataLayer.push({
            event: "vimeo",
            eventCategory: "vimeo",
            eventAction: "vimeo video pause",
            eventLabel: _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){
    }
}

// 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 * 4) / 4).toFixed(2); 
        if (!lastP[data.player_id] || t > lastP[data.player_id]) {
            lastP[data.player_id]=t;
            if (parseFloat(t) != 0){
                dataLayer.push({
                    event: "vimeo",
                    eventCategory: "vimeo",
                    eventAction: "vimeo video " +t*100+ "% Complete",
                    eventLabel: _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){
    }
}

Leave a comment

Filtered HTML

  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.