Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save ja-k-e/d4bc628170c3ec2c35df to your computer and use it in GitHub Desktop.
Save ja-k-e/d4bc628170c3ec2c35df to your computer and use it in GitHub Desktop.
Tracking Interaction in YouTube and Vimeo iFrame APIs
<header>
<div class=container>
<div class=col-full>
<h1>Tracking Interaction in YouTube and Vimeo iFrame APIs</h1>
<p>Interact with the videos below and watch the data update. The behavior for both players is very similar, with a few discrepancies as to when different events fire (see below).</p>
</div>
</div>
</header>
<div class=container>
<div class=col-full>
<p>In the data, <code>sequence</code> is a history of the interaction with completed percentages when the event was triggered. <code>furthest</code> is the highest percentage of completion achieved in the interaction. <code>stops</code> is a count of the amount of times the video complete event fired. These metrics should be enough to derive an accurate picture of how the interaction unfolded; however, they are dependent on the pause, play, and stop events. If you were to navigate from the page while the video is playing, progress would not be updated.</p>
<p>This Vimeo approach uses their suggested <a href="https://github.com/vimeo/player-api/tree/master/javascript" target=blank>froogaloop</a> plugin. Vimeo API Documentation can be found on <a href="https://developer.vimeo.com/player/js-api" target=blank>developer.vimeo.com</a>. YouTube's documentation can be found on <a href="https://developers.google.com/youtube/iframe_api_reference" target=blank>developers.google.com</a>. Oh, and <a href="http://www.ratatatmusic.com/" target=blank>Ratatat</a> is amazing.</p>
</div>
</div>
<div class=container>
<div class=col>
<div id=youtube_player></div>
<pre id=yt-output></pre>
</div>
<div class=col>
<iframe id=vimeo_player src="https://player.vimeo.com/video/125104138?api=1&player_id=vimeo_player" width=500 height=281 frameborder=0 webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
<pre id=vm-output></pre>
</div>
</div>
<footer>
<div class=container>
<div class=col-full>
<h2>Discrepancies</h2>
<ul>
<li>Vimeo player indicates the <code>stop</code> event and <code>play</code> event after <code>stop</code> are at <code>100.2%</code>.</li>
<li>YouTube player indicates <code>stop</code> is at <code>100%</code> and <code>play</code> after <code>stop</code> is at <code>0%</code>.
<li>Vimeo player fires two <code>stop</code> events on finish.</li>
<li>YouTube player fires an additional <code>play</code> event if you skip ahead past the buffer.</li>
<li>YouTube player fires an additional <code>pause</code> event when skipping ahead while paused.</li>
</ul>
</div>
</div>
</footer>
// char entity "icons" for less chars
var i_play = "▸",
i_pause = "▮▮",
i_stop = "▪";
//
// YouTube iFrame
// API documentation:
// https://developers.google.com/youtube/iframe_api_reference
//
// This code loads the IFrame Player API code asynchronously.
var tag = document.createElement('script');
var yt_video_data = {
"sequence": [],
"furthest": 0,
"stops": 0
};
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
// This function creates an <iframe> (and YouTube player)
// after the API code downloads.
var yt_player;
function onYouTubeIframeAPIReady() {
yt_player = new YT.Player('youtube_player', {
height: '281',
width: '500',
videoId: 'f7wkRET0hbo',
// we only need the state change event
events: {
'onStateChange': onPlayerStateChange
}
});
}
// The API calls this function when the player's state changes.
// The function indicates that when playing a video (state=1),
function onPlayerStateChange(event) {
if (event.data == YT.PlayerState.PLAYING) {
updateYtVideoData(i_play);
} else if (event.data == YT.PlayerState.PAUSED) {
updateYtVideoData(i_pause);
} else if (event.data == YT.PlayerState.ENDED) {
updateYtVideoData(i_stop);
}
}
// tracking interaction data
function updateYtVideoData(which) {
// getting video progress
var progress = ytVideoProgress();
// set furthest if progress is the furthest
yt_video_data.furthest = Math.max(yt_video_data.furthest, progress);
// add current video progress to sequence
yt_video_data.sequence.push([which, progress])
// if video is complete
if (which == i_stop) { yt_video_data.stops++; yt_video_data.furthest = 100; }
// put output in dom
ytPrintData();
}
// printing the video data
function ytPrintData() {
var output = "";
for(var key in yt_video_data) {
output += key + ": " + JSON.stringify(yt_video_data[key]) + "\n";
}
document.getElementById("yt-output").innerHTML = output;
}
// getting video progress in 0-100 percentage value
function ytVideoProgress() {
var ratio = yt_player.getCurrentTime() / yt_player.getDuration(),
percent = ratio * 100,
round_percent = Math.round(percent * 10) / 10;
return round_percent;
}
// initial call
ytPrintData();
//
// Vimeo player
// requires Vimeo's froogaloop
// API documentation:
// https://developer.vimeo.com/player/js-api
//
var vm_video_data = {
"sequence": [],
"furthest": 0,
"stops": 0
};
var player = document.getElementById('vimeo_player');
$f(player).addEvent('ready', ready);
// crossbrowser event listener, thanks vimeo
function addEvent(element, eventName, callback) {
if (element.addEventListener) {
element.addEventListener(eventName, callback, false);
}
else {
element.attachEvent(eventName, callback, false);
}
}
// when player is ready
function ready(player_id) {
var player = $f(player_id),
duration = 0;
player.addEvent('pause', onPause);
player.addEvent('finish', onStop);
player.addEvent('play', onPlay);
player.api('getDuration', function (value, id) {
duration = value;
});
function onPlay(id) { updateVmVideoData(i_play); }
function onPause(id) { updateVmVideoData(i_pause); }
function onStop(id) { updateVmVideoData(i_stop); }
// called on each event
function updateVmVideoData(which) {
player.api('getCurrentTime', function (time, id) {
// video progress
var progress = vmVideoProgress(time);
// set furthest value if progress is greater
vm_video_data.furthest = Math.max(vm_video_data.furthest, progress);
// add current data to sequence
vm_video_data.sequence.push([which, progress]);
// if video has ended
if(which == i_stop) { vm_video_data.stops++; vm_video_data.furthest = 100.2;};
// print vimeo data in dom
vmPrintData();
});
}
// yielding a progress in 0-100 percentage format
function vmVideoProgress(time) {
var ratio = time / duration,
percent = ratio * 100,
round_percent = Math.round(percent * 10) / 10;
return round_percent;
}
// printing the video data
function vmPrintData() {
var output = "";
for(var key in vm_video_data) {
output += key + ": " + JSON.stringify(vm_video_data[key]) + "\n";
}
document.getElementById("vm-output").innerHTML = output;
}
// initial print
vmPrintData();
}
jakealbaughSignature();
<script src="https://f.vimeocdn.com/js/froogaloop2.min.js"></script>
// all of this is for the demo. you can ignore.
@import url(http://fonts.googleapis.com/css?family=Open+Sans:300);
body {
background: white;
color: #111;
line-height:1.6;
font-family: "Open Sans", sans-serif;
font-weight: 300;
text-shadow: 1px 1px 0px rgba(255,255,255,0.2);
}
header {
background: #FFBB00;
margin-bottom: 3em;
padding: 4em 0 2em;
border-bottom: 1px solid rgba(255,255,255,0.2);
box-shadow: 0px 0px 12px 4px rgba(0,0,0,0.1);
h1 {
margin-bottom: 1em;
}
p { margin: 0; }
}
footer {
padding: 2em 0;
margin-top: 2em;
background: #111;
color: white;
text-shadow: none;
ul { margin: 0; padding-left: 1.5em; }
h2 { margin-bottom: 1em; margin-top: 0; }
}
h1 {
font-weight: 300;
line-height: 1.2;
margin: 0 0 1.5em;
}
a { color: #ffbb00; text-decoration: none; }
iframe {
margin: 2em auto;
width: 100%;
display: block;
}
iframe, pre {
box-shadow: 0px 4px 12px 0px rgba(0,0,0,0.1);
}
code {
padding: 0.15em 0.25em 0.25em;
margin-top: -0.3em;
display: inline-block;
font-size: 0.8em;
border-radius: 2px;
line-height: 1;
background: #222;
}
pre, code {
color: #FFBB00;
text-shadow: none;
}
pre {
font-size: 12px;
width: 100%;
margin: 0 auto;
padding: 1em;
box-sizing: border-box;
background: #111;
}
.container {
margin: 0 auto;
width: 90%;
&::after { content: ''; display: table; clear: both; }
}
.col, .col-full {
width: 100%;
margin: 0 auto;
box-sizing: border-box;
padding: 0px 10px;
}
@media (min-width: 1040px) {
.col { float: left; width: 50%; padding: 0 10px 2em; }
.container { width: 1040px; }
}

Tracking Interaction in YouTube and Vimeo iFrame APIs

An example of recording iframe-embedded video player states for both YouTube and Vimeo.

A Pen by Jake Albaugh on CodePen.

License.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment