Hello and welcome to our community! Is this your first visit?
Register
Enjoy an ad free experience by logging in. Not a member yet? Register.
Results 1 to 10 of 10
  1. #1
    Regular Coder
    Join Date
    Jun 2006
    Posts
    225
    Thanks
    6
    Thanked 3 Times in 3 Posts

    Object-oriented timers

    I'm just learning object-oriented Javascript, and I decided to try making some timers with it.

    My object is below, heavily commented for your benefit.

    PHP Code:
    function timer() {
      
    this.startMins;
      
    this.startSecs//The time to start the timer at
      
    this.mins;
      
    this.secs//The current amount of time displayed
      
    this.action//1 for count-up timers, -1 for count-down
      
    this.msg//Message to display when count-down reaches 0
      
    this.prefix//Prefix for IDs of timers
      
    this.stopBtn document.getElementById(this.prefix "Stop");
      
    this.startBtn document.getElementById(this.prefix "Start");
      
    this.resetBtn document.getElementById(this.prefix "Reset");
      
    this.display document.getElementById(this.prefix "Display");
      
    this.intervalId//Identifier for the interval to update timer
      
    this.start = function() {
        
    this.startBtn.disabled=1;
        
    this.stopBtn.disabled=0;
        
    this.resetBtn.disabled=0//Disable necessary buttons
        
    this.intervalId setInterval("this.count()",1000); //Start timer
      
    }
      
    this.count = function() {
        eval(
    this.secs += this.action); //Count up or down
        
    if (this.secs >= 60) {
          
    this.secs 0;
          
    this.mins++; //Convert to minutes/seconds
        
    }
        if (
    this.secs 0) {
          if (
    this.mins 0) {
            
    this.secs 59;
            
    this.mins--;
          }
          else {
            
    alert(this.msg); //Alert msg if count-down is at 0
          
    }
        }
        
    this.displayOutput();
      }
      
    this.stop = function() {
        
    clearInterval(intervalId); //Stop timer
        
    this.startBtn.disabled=0;
        
    this.stopBtn.disabled=1//Disable buttons
      
    }
      
    this.reset = function() }
        
    this.startBtn.disabled=0;
        
    this.stopBtn.disabled=1;
        
    this.resetBtn.disabled=1;
        
    this.mins this.startMins;
        
    this.secs this.startSecs//Reset timer to initial values
        
    this.displayOutput(); //Update display to reflect changes
      
    }    
      
    this.displayOutput = function() {
        
    this.display.innerHTML this.mins+":"+this.secs//Display time
      
    }

    I'm unsure what I need to do next. Please read over my code, ensure that it is well-written before I develop any bad habits. Also, performance is the most important aspect of this object, so I would like to optimise it for performance as much as possible.

    I've basically done all I can do on that object from the knowledge I have, and I believe that is the main code that I need. I'm not sure how I would go about making an actual object to use on my page.

    The idea was that I could have HTML forms on my page that showed the timers.

    PHP Code:
    <span id="xxDisplay"></span>
    <
    input type="text" id="xxStop" value="Stop" />
    <
    input type="text" id="xxStart" value="Start" />
    <
    input type="text" id="xxReset" value="Reset" /> 
    Notice that the prefix in this case would be "xx". Then I would initialise the object, and set all necessary values.

  • #2
    Senior Coder
    Join Date
    Nov 2006
    Posts
    1,000
    Thanks
    0
    Thanked 0 Times in 0 Posts
    My main comment is that you have your functions defined in the object itself instead of in its prototype. You have

    function constructor() {
    this.func = function () {
    /* blah */
    };
    }

    This will create a copy of this anonymous function for every instance of the object even though (typically) that function will be the same regardless of the object. The way it is usually done is like this.

    function constructor() {
    }

    constructor.prototype.func = function () {
    /* blah */
    };

    Which will have just one version of the function for all the objects instead of one for each object.

    david_kw

  • #3
    Regular Coder
    Join Date
    Jun 2006
    Posts
    225
    Thanks
    6
    Thanked 3 Times in 3 Posts
    Ok, thanks. Do I call the function the same way (ie. objectName.functionName()?

  • #4
    Senior Coder
    Join Date
    Nov 2006
    Posts
    1,000
    Thanks
    0
    Thanked 0 Times in 0 Posts
    Yes it is called exactly the same way.

    david_kw

  • #5
    Regular Coder
    Join Date
    Jun 2006
    Posts
    225
    Thanks
    6
    Thanked 3 Times in 3 Posts
    Code:
    timer.prototype.start = function() {
      this.startBtn.disabled=1;
      this.stopBtn.disabled=0;
      this.resetBtn.disabled=0;
      this.intervalId = setInterval('this.count()',1000);
    }
    I've run into a problem setting the interval. The interval causes the error "this.count is not a function". I've also tried not quoting the function, and using function() { this.count(); }

    Using object.count() through Firebug works fine.
    Last edited by name _F1; 04-20-2007 at 11:56 AM.

  • #6
    Senior Coder
    Join Date
    Nov 2006
    Posts
    1,000
    Thanks
    0
    Thanked 0 Times in 0 Posts
    Yes that is a tricky problem. The issue is when the interval function is called, "this" is the timer object, not your object. So you are trying to call the method setInterval.count() essentially.

    One solution is to use javascript closure. The way that would work is by saving the "this" value in a variable inside the constructor function. Then use that variable in the function like this.

    Code:
    timer.prototype.start = function() {
      this.startBtn.disabled=1;
      this.stopBtn.disabled=0;
      this.resetBtn.disabled=0;
      var thisTimerObj = this;
      this.intervalId = setInterval(
          function () { thisTimerObj.count(); },
          1000);
      );
    };
    What happens here is the variable thisTimerObj is saved with the timer object being instantiated. And since the anonymous function is defined within the constructor, a copy of this local variable is saved so it can be used in the function.

    Hopefully that works for you. Closure is a difficult thing to describe.

    david_kw

  • #7
    Regular Coder
    Join Date
    Jun 2006
    Posts
    225
    Thanks
    6
    Thanked 3 Times in 3 Posts
    My revised code:

    Code:
    function timer(sm,ss,a,m,p) {
      this.startMins=this.mins=sm;
      this.startSecs=this.secs=ss;
      this.action=a;
      this.msg = m;
      this.prefix = p;
      this.stopBtn = document.getElementById(this.prefix + "stop");
      this.startBtn = document.getElementById(this.prefix + "start");
      this.resetBtn = document.getElementById(this.prefix + "reset");
      this.display = document.getElementById(this.prefix + "display");
      this.intervalId;
      this.benchmark = 0;
      this.loops = 0;
    }
    
    timer.prototype.count = function() {
      eval(this.secs += this.action);
      if (this.secs >= 60) {
        this.secs = 0;
        this.mins++;
      }
      if (this.secs < 0) {
        if (this.mins > 0) {
          this.secs = 59;
          this.mins--;
        }
        else {
          thisTimer = this;
          alert(thisTimer.msg);
          this.stop();
        }
      }
      this.displayOutput();
    }
    
    timer.prototype.start = function() {
      this.displayOutput();
      this.startBtn.disabled=1;
      this.stopBtn.disabled=0;
      this.resetBtn.disabled=0;
      var thisTimer = this;
      this.intervalId = setInterval(function() {thisTimer.count()},1000);
    }
    
    timer.prototype.stop = function() {
      thisTimer = this;
      clearInterval(thisTimer.intervalId);
      this.startBtn.disabled=0;
      this.stopBtn.disabled=1;
    }
    
    timer.prototype.reset = function() {
      this.stop();
      this.resetBtn.disabled=1;
      this.mins = this.startMins;
      this.secs = this.startSecs;
      this.displayOutput();
    }
    
    timer.prototype.displayOutput = function() {
      this.display.innerHTML = this.mins+":"+formatTime(this.secs);
    }
    
    function formatTime(i) {
      if (i<10) 
        {i = "0" + i}
      return i
    }
    I'm sure I used a lot of closures where they aren't needed. Please, again, look over my code and point out any errors or inefficiencies.

    I also have some pages that will be using this script that don't have buttons; instead, the operation of the calculator is done through other scripts. What is the best way of changing my code so that buttons aren't always required?

  • #8
    Senior Coder
    Join Date
    Nov 2006
    Posts
    1,000
    Thanks
    0
    Thanked 0 Times in 0 Posts
    Code:
    function timer(sm,ss,a,m,p) {
      this.startMins=this.mins=sm;
      this.startSecs=this.secs=ss;
      this.action=a;
      this.msg = m;
      this.prefix = p;
      this.stopBtn = document.getElementById(this.prefix + "stop");
      this.startBtn = document.getElementById(this.prefix + "start");
      this.resetBtn = document.getElementById(this.prefix + "reset");
      this.display = document.getElementById(this.prefix + "display");
      this.intervalId;
      this.benchmark = 0;
      this.loops = 0;
    }
    
    timer.prototype.count = function() {
      // eval(this.secs += this.action);
      this.secs += this.action;
      // not sure whey you want the eval around it.  It doesn't seem to do anything
      if (this.secs >= 60) {
        this.secs = 0;
        this.mins++;
      }
      if (this.secs < 0) {
        if (this.mins > 0) {
          this.secs = 59;
          this.mins--;
        }
        else {
          // thisTimer = this;
          // alert(thisTimer.msg);
          alert(this.msg);
          // don't need closure here because the code is executed "now" and not later so 'this' is fine
          this.stop();
        }
      }
      this.displayOutput();
    }
    
    timer.prototype.start = function() {
      this.displayOutput();
      this.startBtn.disabled=1;
      this.stopBtn.disabled=0;
      this.resetBtn.disabled=0;
      var thisTimer = this;
      this.intervalId = setInterval(function() {thisTimer.count()},1000);
      // looks good
    }
    
    timer.prototype.stop = function() {
      // thisTimer = this;
      // clearInterval(thisTimer.intervalId);
      clearInterval(this.intervalId);
      // again this function happens right away so closure isn't needed
      this.startBtn.disabled=0;
      this.stopBtn.disabled=1;
    }
    
    timer.prototype.reset = function() {
      this.stop();
      this.resetBtn.disabled=1;
      this.mins = this.startMins;
      this.secs = this.startSecs;
      this.displayOutput();
    }
    
    timer.prototype.displayOutput = function() {
      this.display.innerHTML = this.mins+":"+formatTime(this.secs);
    }
    
    function formatTime(i) {
      if (i<10) 
        {i = "0" + i}
      return i
    }
    Those are the bits I noticed on my glance through. One other thing is that you forgot the "var" in front of your closure variable for a couple of the ones that weren't needed. You remembered it on the one where it was needed. If you don't put the "var" in then the variable will be global and that could mess you up.

    All my changes should be in red.

    Hope that helps.

    david_kw

  • #9
    Regular Coder
    Join Date
    Jun 2006
    Posts
    225
    Thanks
    6
    Thanked 3 Times in 3 Posts
    Thank you for the help. Any ideas on making the start/stop/reset buttons optional (in terms of disabling them) but not required?

  • #10
    Senior Coder
    Join Date
    Nov 2006
    Posts
    1,000
    Thanks
    0
    Thanked 0 Times in 0 Posts
    I suppose instead of disabling the button you could keep the state in the object.

    like in the constructor put

    Code:
    this.playing = false;
    this.atBeginning = true;
    then in each function do the appropriate checks like

    Code:
    timer.prototype.start = function() {
      if (this.playing) {
        return;
      }
      this.displayOutput();
      this.playing = true;
      this.atBeginning = false;
      var thisTimer = this;
      this.intervalId = setInterval(function() {thisTimer.count()},1000);
    }
    Something like that. So the object will keep its own start instead of counting on the buttons to keep the state for it.

    david_kw


  •  

    Posting Permissions

    • You may not post new threads
    • You may not post replies
    • You may not post attachments
    • You may not edit your posts
    •