Subpar Web Dev
Subpar Web Dev

Reputation: 3280

Where is the flaw in my JavaScript

So I'm trying to make an object that handles an HTML video by making it pause when it reaches certain second marks. At every pause there is some type of "next" button that then resumes the video. My nicely commented code should make it more clear what I'm trying to achieve.

HTML:

    <div class="fancyvid">
        <video preload="auto" poster="assets/myposter.png" id="ssvid">
            <source src="assets/myvideo.mp4" type="video/mp4" />
        </video>
        <div class="textlayer" data-pauseat="5">
            <h1>First pause (5 seconds)</h1>
            <p>Here's some text for the first pause event</p>
            <button class="next-btn">Next</button>
        </div>
        <div class="textlayer" data-pauseat="11">
            <h1>Second pause (11 seconds)</h1>
            <p>Here's some text for the second pause event</p>
            <button class="next-btn">Next</button>
        </div>
        <div class="textlayer" data-pauseat="15">
            <h1>Third pause (15 seconds)</h1>
            <p>Here's some text for the third pause event</p>
            <button class="next-btn">Next</button>
        </div>
    </div>
    <div class="button-holder">
        <button id="video-play">Play Interactive Video</button>
        <script type="text/javascript">
            $(function(){
                $('#video-play').click(function () {
                    VidHandler.PlayFromBeginning();
                    $(this).remove();
                });
            })
        </script>
    </div>

Other JavaScript:

$(function(){
    window.VidHandler = (function (vidId) {

        // refrence to VidHandler for passing into certain scopes
        var that = this;

        // get video DOM element
        this.videlem = $('#' + vidId)[0];

        // get container 
        this.$container = $(this.videlem).closest('div.fancyvid');

        // get the reused "Next" button
        this.$nextbtn = this.$container.find('.next-button');

        // start tick at zero
        this.tick = this.videlem.currentTime;

        // add event listener to video for time events
        this.videlem.addEventListener('timeupdate',function(e){
            console.log('currentTime: ' + that.tick); // TEST
            var pfunc = that.PauseFunctions[that.tick];
            if ( pfunc !== undefined ) // if there is a pause function corresponding to this second
            {
                that.videlem.pause();
                pfunc();
            }
            else
            {
                ++that.tick;
            }
            //
        }, false);


        // plays video from beginning
        this.PlayFromBeginning = function ( ) {
            this.videlem.currentTime = 0; 
            this.videlem.play();
        }

        // returns a jQuery element of the HTML layer
        // associated with a particular second mark
        this.GetLayerElement = function ( secondMark )
        {
            return this.$container.find('div.textlayer[data-pauseat="' + secondMark + '"]');
        }

        // maps second indices in the video to events 
        // that are called when the video pauses at that second
        this.PauseFunctions = {
            5: function () {
                var $layer = that.GetLayerElement(5);              
                $layer.show();
                var $nextbtn = $layer.find('.next-btn');
                $nextbtn.click(function () {
                    $layer.hide();
                    that.videlem.play();
                });
            },
            11 : function () {
                console.log("The pause function of the event at the 11-second mark was called");
            },
            15: function () {
                console.log("The pause function of the event at the 10-second mark was called");
            }

        };

        return this;

    })("ssvid");
});

The main problem I'm facing at the moment is that my

 this.videlem.addEventListener('timeupdate',function(e){

event seems to not be firing evenly.

As a test, I did

    var first_millmark = new Date();

    // add event listener to video for time events
    this.videlem.addEventListener('timeupdate',function(e){
        console.log('currentTime: ' + that.tick); // TEST
        console.log("Milliseconds past: " + (new Date() - first_millmark));//TEST

and the output was

vidpage.js:23 currentTime: 0 vidpage.js:24 Milliseconds past: 2279

vidpage.js:23 currentTime: 1 vidpage.js:24 Milliseconds past: 2777

vidpage.js:23 currentTime: 2 vidpage.js:24 Milliseconds past: 3277

vidpage.js:23 currentTime: 3 vidpage.js:24 Milliseconds past: 3527

vidpage.js:23 currentTime: 4 vidpage.js:24 Milliseconds past: 4027

vidpage.js:23 currentTime: 5 vidpage.js:24 Milliseconds past: 4276

vidpage.js:23 currentTime: 5 vidpage.js:24 Milliseconds past: 4281

Yikes! What's going wrong? And is an entirely better way of implementing this pattern that I'm trying to implement?

Upvotes: 2

Views: 102

Answers (2)

dave
dave

Reputation: 64725

timeupdate does not fire once per second:

The event frequency is dependant on the system load, but will be thrown between about 4Hz and 66Hz (assuming the event handlers don't take longer than 250ms to run). User agents are encouraged to vary the frequency of the event based on the system load and the average cost of processing the event each time, so that the UI updates are not any more frequent than the user agent can comfortably handle while decoding the video.

https://developer.mozilla.org/en-US/docs/Web/Events/timeupdate

What you need to do instead, is look at the time indicated by the currentTime attribute when the event has fired, and see if it is greater than or equal to the time of a pause point that has not been triggered yet.

this.pausePoints = [
    {second: 5, func: function() { /* whatever */ }, 
    {second: 15, func: function() { /* whatever */ }
    //etc.
];

this.videlem.addEventListener('timeupdate',function(e){
    if (this.pausePoints.length && this.videlem.currentTime > this.pausePoints[0].seconds) {
        var pausePoint = this.pausePoints.shift();
        pausePoint.func();
        this.videlem.pause();
    }
}.bind(this), false);

Upvotes: 1

Greg Borbonus
Greg Borbonus

Reputation: 1384

Anything can delay the process of javascript, from the memory JS utilize alone to your computer being slow.

But based on your question:

So I'm trying to make an object that handles an HTML video by making it pause when it reaches certain second marks

I would suspect this function would do the trick nicely, I would return it as soon as possible until you've reached a point in which your video should pause.

so, for example:

timesToPause=[100,1000,10000];

function check_timer(cTime){ //return true 

    if(cTime >= timesToPause[0]){
      timesToPause.splice(0, 1); //Remove this timer from timesToPause;
      pauseVideo(); // whatever your function is to pause the video or run your 
      return true;
    }
return false;
}

Now, I've not fully tested this, but the code should pause the video at the timer specified. If, for example, you start the video at 1200 seconds, it would pause the video twice, hitting both timers of 100 and 1000; You may want to add a loop in the remove section incase they skip it, so it only plays once.

Upvotes: 2

Related Questions