1
\$\begingroup\$

As a Java programmer, I'm new to the OOP-Approach of JavaScript and it's hard for me to find the best way to write code based on objects. I read a lot MDN and across the web to find a good way to code with JavaScript.

Just to see how can it look like, I realized the following functions: Add two INPUT-Elements to a video-element to provide a StartTime (second) and a EndTime (second) to enable a looping of the video in this give time range.

I expect there are already nice frameworks for dealing with video-elements (e. g. VideoJS) - but I just want to know how I can improve the object oriented parts of the code, how to handle events nicely, what is a really good approach to create and use objects, to add methods and so on...

Especially the event-handling (dealing with this inside a addEventListener-Function...) is interesting to me.

I hope this question is interesting also for others and somebody experienced can give me some hints.

Here is the code <HTML-site: index.html>:

<html>
<head>
<script src="script.js" type="text/javascript"></script>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

<div id="controls" style="text-align: center"></div>

<div style="text-align: center">

    <video id="video1" width="420">

        <source src="somevideo.webm" type='video/webm' />
    </video>
    <p>
        <input id="startFrom" type="number" name="startFrom" min="0" max="1000" value="0"> <input id="runUntil"
            type="number" name="runUntil" min="0" max="1000" value="0" >
    </p>
</div>
<div id="info" style="text-align: center; margin-top: 20px">
    <input id="currentTime" type="number" min="0" value="0" readonly>
</div>
</body>
</html>

And here is the corresponding JavaScript script.js:

/**
* This script adds two input controls <startFrom>, <runUntil> to a     "VideoPlayer"
* and allows to loop a certain range inside the given video
* 
* Only works with HTML5 compatible browsers
*/
;
(function(window, undefined) {

var document = window.document;

/**
 * Initial function to ensure the DOM-Content of the website is avaiable
 */
function go() {

document.addEventListener("DOMContentLoaded", function() {

    console.log("DOM ready");
    new VideoPlayer();
}, false);

}
;

/**
 * Wrapper-Class for the Video, which provides the video-Element, 
 * the control-button and the EventHandling for changing
 * the startFrom and runUntil-Input-Elements
 */
function VideoPlayer() {

//global variable for the Video-Handler
_Vid_ = new Video("video1");
_Vid_.setControls();

var startFrom = document.getElementById("startFrom");
var runUntil = document.getElementById("runUntil");

startFrom.addEventListener("change", function(event) {

    var value = event.target.value;
    _Vid_.startTime = value;
    console.log("StartFromValue: " + value);

}, false);

runUntil.addEventListener("change", function(event) {

    var value = event.target.value;
    if(value==0)
    _Vid_.endTime = _Vid_.duration;
    else
    _Vid_.endTime = value;
    console.log("RunUntilValue: " + value);

}, false);

}

/**
 * Video-Class (Video-Handler) which provides all fields and methods necessary to
 * steer the video
 * 
 * Also initializes the video-instance with everything needed
 */
function Video(elementId) {

this.elementId = elementId;

if (this.elementId === undefined) {
    console.log('No Video-Element given!');
    return null;
}

console.log("creating element Video");

this.elementId = elementId;
this.videoElement = new Object();
this.videoIsReady = false;
this.startTime = 0.0;
this.endTime = 0.0;
this.duration = 0.0;
this.currentTime = 0.0;
this.source = "none";
this.loopEnabled = false;

this.videoElement = document.getElementById(this.elementId);
this.videoElement.addEventListener("loadedmetadata", function() {

    console.log("metadata loaded...");
    _Vid_.videoIsReady = true;
    _Vid_.endTime = _Vid_.videoElement.duration;
    _Vid_.duration = _Vid_.videoElement.duration;
    _Vid_.source = _Vid_.videoElement.currentSrc.toString();

    console.log(_Vid_.getInfos());

}, false);

//this Event and it's function realizes the loop capability 
this.videoElement.addEventListener("timeupdate", function() {

    var ct = _Vid_.videoElement.currentTime.valueOf();

    _Vid_.currentTime = ct;

    var ctElement = document.getElementById("currentTime");
    ctElement.setAttribute("value", ct);

    if (ct >= _Vid_.endTime) {
    _Vid_.videoElement.currentTime = _Vid_.startTime;
    console.log("LOOP");
    }

}, false);

this.getInfos = function getInfos() {

    console.log("getInfos()");

    var outputTxt = "StartTime: " + _Vid_.startTime;
    outputTxt += "\nDuration: " + _Vid_.duration;
    outputTxt += "\nSource: " + _Vid_.source;

    return outputTxt;

};

}

//another kind of how to set up a class-method
Video.prototype.setControls = function() {

var playPauseBtn = new VideoControl("playPause", "Play/Pause", "click", function() {

    if (_Vid_.videoElement.paused)
    _Vid_.videoElement.play();
    else
    _Vid_.videoElement.pause();

});

var layer = document.getElementById("controls");
layer.appendChild(playPauseBtn.btn);

};

//Class for realizing VideoControl-Buttons
function VideoControl(name, text, event, fn) {

this.name = name;
this.text = text;
this.event = event;
this.btn = document.createElement("button");

var txt = document.createTextNode(text);
this.btn.appendChild(txt);

this.btn.addEventListener(event, fn, false);

}

//call the start function
go();

})(window);
\$\endgroup\$
3
  • \$\begingroup\$ I realize your program is in vanilla JS but if you check out the following video on handling events in jQuery, it just might provide some clarity on the JS subject. You should check out the methods covered in the jQuery source code so you can get a deeper understanding of how you can apply them to your JS. training.bocoup.com/screencasts/more-efficient-event-handlers \$\endgroup\$ Mar 20 '13 at 15:09
  • \$\begingroup\$ Thank you for this nice hint/link! Going to dive in... So far I didn't want to use any framework to understand the core javascript better (good approach?)... \$\endgroup\$ Mar 20 '13 at 15:34
  • \$\begingroup\$ Yeah! Great way to approach the subject. I posted that link so you can see how the guys at jQuery do it. Not that you should use their framework, but so you can see this in a practical real world example. \$\endgroup\$ Mar 20 '13 at 15:38
2
\$\begingroup\$

From a once over:

  • Not sure if this occurred during the copy pasting of your code, but the indentation is bad, for example this:

    //another kind of how to set up a class-method
    Video.prototype.setControls = function() {
    
    var playPauseBtn = new VideoControl("playPause", "Play/Pause", "click", function() {
    
        if (_Vid_.videoElement.paused)
        _Vid_.videoElement.play();
        else
        _Vid_.videoElement.pause();
    
    });
    
    var layer = document.getElementById("controls");
    layer.appendChild(playPauseBtn.btn);
    
    };
    

    should be this:

      //another kind of how to set up a class-method
      Video.prototype.setControls = function() {
    
        var playPauseBtn = new VideoControl("playPause", "Play/Pause", "click", function() {
    
            if (_Vid_.videoElement.paused)
                _Vid_.videoElement.play();
            else
                _Vid_.videoElement.pause();
    
        });
    
        var layer = document.getElementById("controls");
        layer.appendChild(playPauseBtn.btn);
    
      };
    
  • You could also exchange the if in that snippet with a ternary operation
  • You only use layer once, you might as well merge that line with the next one
  • That would give:

      Video.prototype.setControls = function() {
    
        var playPauseBtn = new VideoControl("playPause", "Play/Pause", "click", function() {
            Vid_.videoElement.paused ? _Vid_.videoElement.play() : _Vid_.videoElement.pause();
        });
        document.getElementById("controls").appendChild(playPauseBtn.btn);
      };
    
  • JsHint.com could not find anything serious
  • Do not use console.log in production code
  • So that this: runUntil.addEventListener("change", function(event) {

        var value = event.target.value;
        if(value==0)
        _Vid_.endTime = _Vid_.duration;
        else
        _Vid_.endTime = value;
        console.log("RunUntilValue: " + value);
    
    }, false);
    

    can be

    runUntil.addEventListener("change", function(event) {
        var value = event.target.value;
        _Vid_.endTime = value ? value : _Vid_.duration;
    }, false);
    
  • Since you put everything into an IIFE, you could put 'use strict'; after(function(window, undefined) {
  • _Vid_ is a bad name, don't overdo the underscores
  • Since you should not do console.log in production code, you don't need this.getInfos at all.

All in all, I think your usage of _Vid_ breaks the OOP approach, I am pretty sure that you cannot run 2 instances of the video element which means it's broken.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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