23

Does anyone knows how I can check if a video was completely watched or not? I am using html5 video players:

<video width="480" height="400" controls="true" poster="">
    <source type="video/mp4" src="video.mp4"></source>
</video>
2
  • Here is one solution i found, thanks all document.getElementById('myVideo').addEventListener('ended',myHandler,false); function myHandler(e) { alert("finished"); // What you want to do after the event }
    – mrana
    Commented May 17, 2016 at 10:04
  • Are you trying to determine whether every second of the video was played (no way to know if it was watched), or playing terminated at the end, or what? Your requirements aren't clear.
    – fixer1234
    Commented May 18, 2016 at 2:57

5 Answers 5

41

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');
8
  • it is actually a nice answer. can you please help me to turn off the video seeking option? so no one can seek forward.
    – mrana
    Commented May 19, 2016 at 11:32
  • 3
    @mrana It's considered impolite, here on StackOverflow, to ask things that were not part of your original question. If you think disabling video seeking is not explained anywhere on the internet (my bets are it is) ask another question about that. Commented May 19, 2016 at 11:51
  • Great code this really helped me out a lot, I just wanted to make a note for anyone else using this code in the future. If the user pauses the video a few times and it is possible to round the seconds down instead of up you will never get a "complete" message. Math.round(timePlayed)+""; rounds to nearest whole number (up or down).... change this to Math.ceil(timePlayed)+""; and it will only round up to solve this bug.
    – zcleft
    Commented Jan 16, 2017 at 3:11
  • The time isn't being rounded for the calculation - only for displaying the sum in the HTML. Changing round to ceil here changes nothing. Commented Jan 16, 2017 at 5:57
  • 2
    How to do this with youtube embedded video?
    – Tasaduq H.
    Commented Nov 26, 2018 at 11:52
6

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
});
3

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).

1

You can use:

function getPlayedTime(video) {
    let totalPlayed = 0;
    const played = video.played;

    for (let i = 0; i < played.length; i++) {
        totalPlayed += played.end(i) - played.start(i);
    }

    return {
        total: totalPlayed,
        percent: totalPlayed / video.duration * 100,
    };
}
0
var vid = document.getElementById("myVid");
vid.onended = function() {alert("The video has ended");};

Not the answer you're looking for? Browse other questions tagged or ask your own question.