2

Il try to explain this as simple as I can.

I'm trying to make a timer app for practice. User should be abble to add a new Object, wich will be a timer + stop/start buttons.

At this point, its working with one object, but when I create another object, the old one stops working(i belive it loses the pointer to it, or somthing like that), and the new one works.

Dont worry, about the buttons functionality,thats not important, all I want to understand how do I save the object, after a new one is created.

here is JS:

var startTime = 0;
var itemNum = 0;
var obName;
var arrayForTimers = [];

function timerObject(obName) {
this.nameField = "start";
this.classField = "timer";
this.block = " ";
this.obName = obName;
this.startButton = " ";
this.stopButton = " ";
this.placeholder = document.getElementsByClassName("timer-placeholder")[0];
this.addBlock = function () {
    this.block = document.createElement("DIV");
    this.block.className = this.classField;
    this.block.className += " " + this.obName;
    this.placeholder.appendChild(this.block);
}
this.renderTime = function () {
    this.startTime = startTime;
    this.endTime = new Date();
    // time difference in ms
    this.timeDiff = this.endTime - this.startTime;
    // strip the miliseconds
    this.timeDiff /= 1000;
    // get seconds
    this.seconds = Math.round(this.timeDiff % 60);
    // remove seconds from the date
    this.timeDiff = Math.floor(this.timeDiff / 60);
    // get minutes
    this.minutes = Math.round(this.timeDiff % 60);
    // remove minutes from the date
    this.timeDiff = Math.floor(this.timeDiff / 60);
    // get hours
    this.hours = Math.round(this.timeDiff % 24);
    this.block.innerHTML = this.hours + " h " + this.minutes + " min " + this.seconds + " sec";
    // window.setInterval('this.obName.renderTime()',1000);// Uncomment if you want to test your PCs limit
}
this.renderButtons = function () {
    var timePassed;
    //this.parentTimer = document.getElementsByClassName("timer "+this.obName)[0];
    this.startButton = document.createElement("INPUT");
    this.startButton.setAttribute("type", "button");
    this.startButton.setAttribute("value", "start");
    this.startButton.className = "start " + this.obName;
    this.stopButton = document.createElement("INPUT");
    this.stopButton.setAttribute("type", "button");
    this.stopButton.setAttribute("value", "stop");
    this.stopButton.className = "stop " + this.obName;
    this.placeholder.insertBefore(this.startButton, this.block);
    this.placeholder.insertBefore(this.stopButton, this.block);
    this.startButton.addEventListener("click", function () {
        //if (this.hours === 0 && this.minutes === 0 && this.seconds === 0){
        this.startTime = new Date();
        // }
        tm = window.setInterval('obName.renderTime()', 1000);
    })
    this.stopButton.addEventListener("click", function () {
        window.clearInterval(tm);
        //timePassed = this.endTime - this.startTime;

        //endTime = new Date();
        // timePassed = endTime - startTime;
    })
    //give listener to clear and start interval
    }
};

function createNewTimer() {
    obName = document.getElementById("ObName").value;
    if (obName !== "") {
       obName = new timerObject(obName);
       obName.addBlock();
       obName.renderButtons();
       startTime = new Date();
        obName.renderTime();
        //arrayForTimers.push(obName);
       //window.setInterval('obName.renderTime()',1000);
     } else {
       document.getElementById("ObName").value = "Fill me";
   }
};

Here is a Js Fiddle http://jsfiddle.net/3qxoea52/4/

P.S. sorry for my grammar, not my native languege.

Thanks.

Jaru
  • 95
  • 1
  • 4

2 Answers2

1

The problem is here

tm = window.setInterval('obName.renderTime()', 1000);

This means that when you click any of the start buttons it will always start the object currently associated with obName.

Also, you only have a single global variable tm to store the timer, so when you do

window.clearInterval(tm);

It is clearing whichever timer was last set.

And you also have a single global startTime, which is causing all of the timers to reset whenever you create a new timer.

Another problem is that you have nothing to store the time when you stop each timer, so if you stop then restart it, it will not continue at the same point.

You can fix these problems by:

  1. using self = this to create a stable reference to the object
  2. use function.bind to make sure an event handler will be bound to the right timer object
  3. add a property this.startDiff so that the time is saved when you stop then restart the clock
  4. make tm a local variable within the TimerObject
  5. set this.startTime within the object instead of outside it.

-- as in the code below (jsfiddle)

function TimerObject(obName) {
    var self = this,
        tm;
    this.startTime = new Date();
    this.startDiff = 0;
    this.nameField = "start";
    this.classField = "timer";
    this.block = " ";
    this.obName = obName;
    this.startButton = " ";
    this.stopButton = " ";
    this.placeholder = document.getElementsByClassName("timer-placeholder")[0];
    this.addBlock = function () {
        this.block = document.createElement("DIV");
        this.block.className = this.classField;
        this.block.className += " " + this.obName;
        this.placeholder.appendChild(this.block);
    };
    this.renderTime = function () {
        var timeDiff = new Date() - this.startTime + this.startDiff;
        this.timeDiff = timeDiff;
        timeDiff /= 1000;
        // get seconds
        this.seconds = Math.round(timeDiff % 60);
        // remove seconds from the date
        timeDiff = Math.floor(timeDiff / 60);
        // get minutes
        this.minutes = Math.round(timeDiff % 60);
        // remove minutes from the date
        timeDiff = Math.floor(timeDiff / 60);
        // get hours
        this.hours = Math.round(timeDiff % 24);
        this.block.innerHTML = this.hours + " h " + this.minutes + " min " + this.seconds + " sec";
        // window.setInterval('this.obName.renderTime()',1000);// Uncomment if you want to test your PCs limit
    };
    this.renderButtons = function () {
        var timePassed;
        //this.parentTimer = document.getElementsByClassName("timer "+this.obName)[0];
        this.startButton = document.createElement("INPUT");
        this.startButton.setAttribute("type", "button");
        this.startButton.setAttribute("value", "start");
        this.startButton.className = "start " + this.obName;
        this.stopButton = document.createElement("INPUT");
        this.stopButton.setAttribute("type", "button");
        this.stopButton.setAttribute("value", "stop");
        this.stopButton.className = "stop " + this.obName;
        this.placeholder.insertBefore(this.startButton, this.block);
        this.placeholder.insertBefore(this.stopButton, this.block);
        this.startButton.addEventListener("click", function () {
            //if (this.hours === 0 && this.minutes === 0 && this.seconds === 0){
            self.startTime = new Date();
            // }
            tm = window.setInterval(self.renderTime.bind(self), 1000);
        });
        this.stopButton.addEventListener("click", function () {
            window.clearInterval(tm);
            self.startDiff = self.timeDiff;
            console.log(':', self, self.startDiff);
            //timePassed = this.endTime - this.startTime;

            //endTime = new Date();
            // timePassed = endTime - startTime;
        });
        //give listener to clear and start interval
    };
}


function createNewTimer() {
    obName = document.getElementById("ObName").value;
    if (obName !== "") {
        obName = new TimerObject(obName);
        obName.addBlock();
        obName.renderButtons();
        obName.renderTime();
        //window.setInterval('obName.renderTime()',1000);
    } else {
        document.getElementById("ObName").value = "Fill me";
    }
}
Stuart
  • 7,007
  • 1
  • 16
  • 25
  • Could you please explain me this? using self = this to create a stable reference to the object, how is it affecting the code? – Jaru Mar 07 '15 at 14:28
  • see http://stackoverflow.com/questions/962033/what-underlies-this-javascript-idiom-var-self-this and http://alistapart.com/article/getoutbindingsituations. When javascript calls event handlers it doesn't automatically bind them to the desired context, so `this` ends up referring to the window instead of your object; you can either use `.bind` or `self` or (as in the code here) some combination of the two. `self` is defined within the constructor function and so maintains the same value in all code within the constructor function, even though the context may change. – Stuart Mar 07 '15 at 18:41
1

There were several issues with the code: 1. Some functions weren't being bound to the correct context (this). 2. The reference set to "obName" was global and constantly changing to the newly created instance. 3. The start time variable was global and constantly being reset every time you called the renderTime function.

I have made the modifications to your code in jsFiddle and they can be found in this update: http://jsfiddle.net/3qxoea52/5/

Rendering:

this.renderTime = function (startTime) {
    if( startTime ) {
        this.startTime = startTime;
    }
    this.endTime = new Date();
    // time difference in ms
    this.timeDiff = this.endTime - this.startTime;
    // strip the miliseconds
    this.timeDiff /= 1000;
    // get seconds
    this.seconds = Math.round(this.timeDiff % 60);
    // remove seconds from the date
    this.timeDiff = Math.floor(this.timeDiff / 60);
    // get minutes
    this.minutes = Math.round(this.timeDiff % 60);
    // remove minutes from the date
    this.timeDiff = Math.floor(this.timeDiff / 60);
    // get hours
    this.hours = Math.round(this.timeDiff % 24);
    this.block.innerHTML = this.hours + " h " + this.minutes + " min " + this.seconds + " sec";
    // window.setInterval('this.obName.renderTime()',1000);// Uncomment if you want to test your PCs limit
}

Binding:

this.startButton.addEventListener("click", function () {
            //if (this.hours === 0 && this.minutes === 0 && this.seconds === 0){
            this.startTime = new Date();
            // }
            this.tm = window.setInterval(this.renderTime.bind(this), 1000);
        }.bind(this))
        this.stopButton.addEventListener("click", function () {
            window.clearInterval(this.tm);
            //timePassed = this.endTime - this.startTime;

            //endTime = new Date();
            // timePassed = endTime - startTime;
        }.bind(this))

CreateTimer:

function createNewTimer() {
    obName = document.getElementById("ObName").value;
    if (obName !== "") {
        obName = new timerObject(obName);
        obName.addBlock();
        obName.renderButtons();
        startTime = new Date();
        obName.renderTime(startTime);
        //window.setInterval('obName.renderTime()',1000);
    } else {
        document.getElementById("ObName").value = "Fill me";
    }
};
Eyal
  • 494
  • 2
  • 8