Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Video 'timeupdate' listener forgets values

I have a html5 video event listener that is supposed to wait until the correct time and then pause the video while the user takes part in a quiz. The first 'lesson' works fine, and the second video also appears to add the listener with the correct time to pause. But upon playing the second video it always pauses at 170 seconds, the pause time from the FIRST video.

Also, when I check Chrome's dev panel it actually shows timeCache as having immediately reverted back to the previous videos values as soon as the video is played; unless the video has passed the 170 mark, then it will use the 230 second timeCache value as it should. At first I thought it was because the old event listener was still attached, but I have eliminated that possibility and the problem still persists. Here is the link http://koreanwordgame.com/grammar/

var setPause = function (time) {
   var video = $("video").get(0);
   var timeCache = time;
   video.removeEventListener('timeupdate', timeListener, false);
   function timeListener (){
    if (video.currentTime >= timeCache && video.currentTime < (timeCache + 0.3)) {
        video.pause();
    }}
   video.addEventListener('timeupdate', timeListener);
};

the first $watch in the directive is triggered each time a new lesson is loaded, it binds the ended event as well as the timeupdate listener via setPause() and then loads and plays the video. The idea is that setPause sets the time that the video will automatically pause at when it reaches, and then the second $watch waits until all the questions have been answered before playing the remainder of the video (generally a congratulations message)

app.directive('videoWatcher', function () {
return function (scope, video, attrs) {
    scope.$watch(attrs.videoLoader, function () {
        $(video[0]).bind('ended', function () {
            $(this).unbind('ended');
            if (!this.ended) {
                return;
            }
            scope.tutorialNumber++;
            scope.$apply();
            scope.loadFromMenu();
        });
        setPause(scope.currentTutorial.pause);
        video[0].load();
        video[0].play();
    });
    scope.$watch(attrs.congrats, function(){
        var cT = scope.currentTutorial;
        if (scope.questionNumber === cT.material.length){
            video[0].play();
            setTimeout(function () {
                video[0].play();
            }, 500);
        }
    });
};
})
like image 398
Tules Avatar asked Feb 08 '14 15:02

Tules


1 Answers

Every time you call your pause function, you create a new instance of the timeListener function. Any reference to timeListener is a reference to the one you just created. So when you're removing the event listener, you're removing the new function, not the one you attached before.

In Javascript, within a given function, it doesn't matter where you declare variables and functions; they are always "hoisted" to the top. So even though you write the timeListener function after your call to removeEventListener, your code behaves as though you declared it at the top of pause. This is why it's usually a good idea to declare all your variables and functions before running any other code (and JSLint will give you a hard time if you don't). The exception is when you explicitly assign a function to a variable.

You can fix this by declaring timeListener outside of pause, so it will always be a reference to the previous instance. Like this:

var timeListener;
function pause(time) {
    //timeCache is unnecessary
    var video = $("video").get(0),
        end = time + 0.3; //cache this so you don't have to add every time

    if (timeListener) {
        //remove previous timeListener function, if there is one
        video.removeEventListener('timeupdate', timeListener, false);
    }

    //make a new function and save it for later as timeListener
    timeListener = function () {
        if (video.currentTime >= time && video.currentTime < end) {
            video.pause();
        }
    };
    video.addEventListener('timeupdate', timeListener);
};
like image 102
brianchirls Avatar answered Nov 02 '22 03:11

brianchirls