Jeffrey James Jeffrey James - 3 months ago 34
Javascript Question

How to Fix HTML5 Video Javascript Tracking Code That is not Working Correctly

I have some JavaScript code I found online that provides stats to google analytics for my HTML5 video. The code however only CORRECTLY displays stats for "video played" and "video paused" but the rest of the information won't display or even calculate. The rest of the info is:

"25% video watched",
"50% video watched",
"75% video watched",
"100% video watched".

How can I get the code below working properly? Also, is google analytics the only way to track these stats or is there another way?



<script type="text/javascript">
document.addEventListener('DOMContentLoaded', init, false)
var videoId = document.getElementById('bigvid3')
var videoTitle = videoId.getAttribute('data-description')
var videoTitle = 'bigvid3'

function init () {
videoId.addEventListener('play', videoPlay, false)
videoId.addEventListener('pause', videoPause, false)
videoId.addEventListener('ended', videoEnd, false)
videoId.addEventListener('timeupdate', videoTimeUpdate, false)

}

function setKeyFrames (duration) {
var quarter = (duration / 4).toFixed(1)
sessionStorage.setItem('one', quarter)
sessionStorage.setItem('two', (quarter * 2).toFixed(1))
sessionStorage.setItem('three', (quarter * 3).toFixed(1))
}

function videoTimeUpdate () {
var curTime = videoId.currentTime.toFixed(1)
switch (curTime) {
case sessionStorage.getItem('one'):
ga('send', 'event', 'video', '25% video played', videoTitle)
sessionStorage.setItem('one', null)
case sessionStorage.getItem('two'):
ga('send', 'event', 'video', '50% video played', videoTitle)
sessionStorage.setItem('two', null)
case sessionStorage.getItem('three'):
ga('send', 'event', 'video', '75% video played', videoTitle)
sessionStorage.setItem('three', null)
}
}

function videoPlay () {
ga('send', 'event', 'video', 'video played', videoTitle)
setKeyFrames(this.duration)
}

function videoPause () {
ga('send', 'event', 'video', 'video paused', videoTitle)
}

function videoTimeUpdate () {
ga('send', 'event', 'video', '25% video played', '50% video played', '75% video played', videoTitle)
}

function videoTimeUpdate () {
ga('send', 'event', 'video', '25% video played', videoTitle)
}

function videoTimeUpdate () {
ga('send', 'event', 'video', '50% video played', videoTitle)
}

function videoTimeUpdate () {
ga('send', 'event', 'video', '75% video played', videoTitle)
}

function videoEnd () {
ga('send', 'event', 'video', '100% video played', videoTitle)
}
</script>




Answer

Just so that you are aware, this code alone even fixed won't work. There's a really nice tutorial online for this but you seem to have found the wrong one. I'll do my best to simplify the process for you.

First let's fix the code in the original question:

<script type="text/javascript">
      document.addEventListener('DOMContentLoaded', init, false)
var videoId = document.getElementById('bigvid3')
//var videoTitle = videoId.getAttribute('data-description')
var videoTitle = 'bigvid3'

function init () {
	videoId.addEventListener('ended', videoEnd, false)
	videoId.addEventListener('timeupdate', videoTimeUpdate, false)
	videoId.addEventListener('play', videoPlay, false)
	videoId.addEventListener('pause', videoPause, false)
}

function setKeyFrames (duration) {
	var quarter = (duration / 4);
	sessionStorage.setItem('one', quarter);
	sessionStorage.setItem('two', (quarter * 2));
	sessionStorage.setItem('three', (quarter * 3));
}

function videoTimeUpdate () {
    var curTime = videoId.currentTime.toFixed(1)

    if (curTime > sessionStorage.getItem('one') && sessionStorage.getItem('one') != null) {
        ga('send', 'event', 'video', '25% video played', videoTitle)
        sessionStorage.setItem('one', null)
    } else if (curTime > sessionStorage.getItem('two') && sessionStorage.getItem('two') != null) {
            ga('send', 'event', 'video', '50% video played', videoTitle)
            sessionStorage.setItem('two', null)
    } else if (curTime > sessionStorage.getItem('three') && sessionStorage.getItem('three') != null) {
            ga('send', 'event', 'video', '75% video played', videoTitle)
            sessionStorage.setItem('three', null)

    }


function videoEnd () {
	ga('send', 'event', videoCategory, '100% video played', videoTitle);

}

function videoPlay () {
	ga('send', 'event', videoCategory, 'video played', videoTitle);

	setKeyFrames(this.duration);
}

function videoPause (video) {
                        var pauseCurTime = videoId.currentTime,
                        pauseDuration = videoId.duration;
                       ga('send', 'event', videoCategory, 'video paused', videoTitle);
}
    </script>

Next step is to add a google tag manager markup after the opening body tag of the page where the video is found:

<!-- Google Tag Manager -->
<noscript><iframe src="//www.googletagmanager.com/ns.html?id=emblem"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'//www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','emblem');</script>
<!-- End Google Tag Manager -->

Once you have google tag manager set up correctly to trigger/fire the events make sure to replace the world emblem with your actual google tag manager emblem found at the top left of your page.

Finally Add this markup to obtain the functionality you're looking for:

<script>
// Let's wrap everything inside a function so variables are not defined as globals  
(function(){  
    // This is gonna our percent buckets ( 25%-75% )
    //Change the variable "divisor" to create different multiples to track smaller %'s like 10% etc.
    var divisor = 25;  
    // We're going to save our players status on this object.  
    var videos_status = {};  
    // This is the funcion that is gonna handle the event sent by the player listeners  
    function eventHandler(e){   
      switch(e.type) {  
          // This event type is sent everytime the player updated it's current time,  
          // We're using for the % of the video played.     
        case 'timeupdate':      
          // Let's set the save the current player's video time in our status object              
          videos_status[e.target.id].current = Math.round(e.target.currentTime);     
          // We just want to send the percent events once     
          var pct = Math.floor(100 * videos_status[e.target.id].current / e.target.duration);            
          for (var j in videos_status[e.target.id]._progress_markers) {
            if (pct >= j && j > videos_status[e.target.id].greatest_marker) {
    			videos_status[e.target.id].greatest_marker = j;
            }
          }
         // current bucket hasn't been already sent to GA?, let's push it to GTM
         if (videos_status[e.target.id].greatest_marker && !videos_status[e.target.id]._progress_markers[videos_status[e.target.id].greatest_marker]) {
             videos_status[e.target.id]._progress_markers[videos_status[e.target.id].greatest_marker] = true;
             dataLayer.push({
                 'event': 'gaEvent',
                 'gaEventCategory': 'HTML5 Video',
                 'gaEventAction': 'Progress_' + videos_status[e.target.id].greatest_marker + '%', 
                 // We are using sanitizing the current video src string, and getting just the video name part
                 'gaEventLabel': decodeURIComponent(e.target.currentSrc.split('/')[e.target.currentSrc.split('/').length - 1])
             });
        }

    break;
    // This event is fired when user's click on the play button
    case 'play':
        dataLayer.push({
            'event': 'gaEvent',
            'gaEventCategory': 'HTML5 Video',
            'gaEventAction': 'Play',
            'gaEventLabel': decodeURIComponent(e.target.currentSrc.split('/')[e.target.currentSrc.split('/').length - 1])
        });

        break;
        // This event is fied when user's click on the pause button
    case 'pause':
        dataLayer.push({
            'event': 'gaEvent',
            'gaEventCategory': 'HTML5 Video',
            'gaEventAction': 'Pause',
            'gaEventLabel': decodeURIComponent(e.target.currentSrc.split('/')[e.target.currentSrc.split('/').length - 1]),
            'gaEventValue': videos_status[e.target.id].current
        });
        break;
        // If the user ends playing the video, an Finish video will be pushed ( This equals to % played = 100 )  
    case 'ended':
        dataLayer.push({
            'event': 'gaEvent',
            'gaEventCategory': 'HTML5 Video',
            'gaEventAction': 'Finished',
            'gaEventLabel': decodeURIComponent(e.target.currentSrc.split('/')[e.target.currentSrc.split('/').length - 1])
        });
        break;
    default:
        break;
        }

     }
        // We need to configure the listeners
        // Let's grab all the the "video" objects on the current page     
        var videos = document.getElementsByTagName('video');
        for (var i = 0; i < videos.length; i++) {
            // In order to have some id to reference in our status object, we are adding an id to the video objects
            // that don't have an id attribute. 
            var videoTagId;
            if (!videos[i].getAttribute('id')) {
                // Generate a random alphanumeric string to use is as the id
                videoTagId = 'html5_video_' + Math.random().toString(36).slice(2);
                videos[i].setAttribute('id', videoTagId);
            }
            // Current video has alredy a id attribute, then let's use it :)
            else {
                videoTagId = videos[i].getAttribute('id');
            }

            // Video Status Object declaration  
            videos_status[videoTagId] = {};
            // We'll save the highest percent mark played by the user in the current video.
            videos_status[videoTagId].greatest_marker = 0;
            // Let's set the progress markers, so we can know afterwards which ones have been already sent.
            videos_status[videoTagId]._progress_markers = {};

            for (j = 0; j < 100; j++) {
                videos_status[videoTagId].progress_point = divisor * Math.floor(j / divisor);
                videos_status[videoTagId]._progress_markers[videos_status[videoTagId].progress_point] = false;
            } 
            // On page DOM, all players currentTime is 0     
            videos_status[videoTagId].current = 0;      
            // Now we're setting the event listeners.     
            videos[i].addEventListener("play", eventHandler, false);     
            videos[i].addEventListener("pause", eventHandler, false);     
            videos[i].addEventListener("ended", eventHandler, false);     
            videos[i].addEventListener("timeupdate", eventHandler, false);     
            videos[i].addEventListener("ended", eventHandler, false);
         } 
})();
</script>

You will need to add this markup to google tag manager not the page where the video is found and set parameters.

This is a simplified version of this tutorial. If you do it right, you will get what you need.

One last thing. I see absolutely nothing wrong with videoEnd. It should work. Make sure your video is not set to LOOP otherwise it never ends and it will not register. Other than that I cannot see any other possibility that it won't register.