How to check a user watched the full video in html5 video player

Basic check is simple, wait for the ended event. This is so simple you can just google it.

Now to check that user played full video an extensive analysis would be needed checking if he played every second of it. That's not necessary however, it should be enough that user:

  • played as many seconds as the video is long
  • played to the end of the video

This snippet demonstrates exactly that. The video will not be marked as fully played if you just skip to the end. Playing the beginning over and over will also not mark it fully played:

var video = document.getElementById("video");

var timeStarted = -1;
var timePlayed = 0;
var duration = 0;
// If video metadata is laoded get duration
if(video.readyState > 0)
  getDuration.call(video);
//If metadata not loaded, use event to get it
else
{
  video.addEventListener('loadedmetadata', getDuration);
}
// remember time user started the video
function videoStartedPlaying() {
  timeStarted = new Date().getTime()/1000;
}
function videoStoppedPlaying(event) {
  // Start time less then zero means stop event was fired vidout start event
  if(timeStarted>0) {
    var playedFor = new Date().getTime()/1000 - timeStarted;
    timeStarted = -1;
    // add the new number of seconds played
    timePlayed+=playedFor;
  }
  document.getElementById("played").innerHTML = Math.round(timePlayed)+"";
  // Count as complete only if end of video was reached
  if(timePlayed>=duration && event.type=="ended") {
    document.getElementById("status").className="complete";
  }
}

function getDuration() {
  duration = video.duration;
  document.getElementById("duration").appendChild(new Text(Math.round(duration)+""));
  console.log("Duration: ", duration);
}

video.addEventListener("play", videoStartedPlaying);
video.addEventListener("playing", videoStartedPlaying);

video.addEventListener("ended", videoStoppedPlaying);
video.addEventListener("pause", videoStoppedPlaying);
#status span.status {
  display: none;
  font-weight: bold;
}
span.status.complete {
  color: green;
}
span.status.incomplete {
  color: red;
}
#status.complete span.status.complete {
  display: inline;
}
#status.incomplete span.status.incomplete {
  display: inline;
}
<video width="200" controls="true" poster="" id="video">
    <source type="video/mp4" src="http://www.w3schools.com/html/mov_bbb.mp4"></source>
</video>

<div id="status" class="incomplete">
<span>Play status: </span>
<span class="status complete">COMPLETE</span>
<span class="status incomplete">INCOMPLETE</span>
<br />
</div>
<div>
<span id="played">0</span> seconds out of 
<span id="duration"></span> seconds. (only updates when the video pauses)
</div>
Also on jsFiddle: https://jsfiddle.net/p56a1r45/2/

You can then connect this to google analytics to see how many of the video users played. Simple code from google analytics website:

ga('send', 'event', 'Videos', 'play', 'Video name');

Adding an id attribute:

<video id="video" width="480" height="400" controls="true" poster="">
    <source type="video/mp4" src="video.mp4"></source>
</video>

You can attach the event ended to your video:

With plain javascript:

document.getElementById('video').addEventListener('ended', function(e) {
    // Your code goes here
});

With jQuery:

$('#video').bind('ended', function() {
   // Your code goes here
});

Here is a comprehensive solution:

  • User cannot seek forward to not-yet watched parts (which also ensures proper sequence of watching, i.e. no skipping forward and then back)
  • Then one can simply detect the video ending
  • Also: when the window (or tab) loses focus, the video pauses, to make it more likely that the user is actually watching the video throughout
  • Also: it can be easily reset for any number of watchings/videos

(The seek disabling function below comes from How to disable seeking with HTML5 video tag ?)

Assuming you have a video element with id "vid_id" in the HTML, e.g.:

<video id="vid_id" controls>
    <source src="whatever.mp4" type="video/mp4">
</video>

You can use these functions:

function vid_listen() {
    var video = document.getElementById('vid_id');
    video.addEventListener('timeupdate', function() {
        if (!video.seeking) {
            if (video.currentTime > timeTracking.watchedTime) {
                timeTracking.watchedTime = video.currentTime;
                lastUpdated = 'watchedTime';
            } else {
                //tracking time updated  after user rewinds
                timeTracking.currentTime = video.currentTime;
                lastUpdated = 'currentTime';
            }
        }
        if (!document.hasFocus()) {
            video.pause();
        }
    });
    // prevent user from seeking
    video.addEventListener('seeking', function() {
        var delta = video.currentTime - timeTracking.watchedTime;
        if (delta > 0) {
            video.pause();
            //play back from where the user started seeking after rewind or without rewind
            video.currentTime = timeTracking[lastUpdated];
            video.play();
        }
    });
    video.addEventListener("ended", function() {
        // here the end is detected
        console.log("The video has ended");
    });
}
function vid_start() {
    window.timeTracking = {
        watchedTime: 0,
        currentTime: 0
    };
    window.lastUpdated = 'currentTime';
}

Execute vid_listen() any time after the document loaded. Execute vid_start() any time before the video is started (or when a new analogous check is needed).