1

I'm having a problem with the setInterval() Method in Javascript. My main class:

var sq1 = new Square(20, 20);
window.onkeydown = function (e)
{
    var key = e.keyCode ? e.keyCode : e.which;
    if (key == 38)
    {
        sq1.moveUp();
    }
}

And I have following constructor function.

function Square(x,y)
{
    var multiplicator = 10;

    this.xPos = x;
    this.yPos = y;

    this.domObj = document.createElement("div");
    this.domObj.style.position = "absolute";
    this.domObj.style.left = this.xPos * multiplicator + 'px';
    this.domObj.style.top = this.yPos * multiplicator + 'px';
    this.domObj.style.width = multiplicator + 'px';
    this.domObj.style.height = multiplicator + 'px';
    this.domObj.style.backgroundColor = "black";
    document.getElementById("idCanvas").appendChild(this.domObj);

    this.moveUp = function ()
    {
        this.yPos--;
        this.domObj.style.top = (this.yPos * multiplicator) + 'px';
    }
}

Well that works fine by now, just moving every keyUp event 10px up. But I would like to call this.moveUp() every 1000 miliseconds automatically after the keyUp event. But when I try this:

this.moveUp = function ()
{
    setInterval(function ()
    {
        this.yPos--;
        this.domObj.style.top = (this.yPos * multiplicator) + 'px';
    }, 1000);
}

I get an error that 'this' is null.

How can I fix it (Preferably without jQuery)?

Damien Flury
  • 629
  • 10
  • 21

2 Answers2

2

The target is the Window object inside setInterval

You either need to capture the lexical scope and use it or use bind to hard bind the object reference to the handler inside the setInterval scope.

Usage of Lexical scope

this.moveUp = function() {
  // capturing the lexical scope
  var self = this;
  setInterval(function() {
    self.yPos--;
    self.domObj.style.top = (self.yPos * multiplicator) + 'px';
  }, 1000);
}

Using bind

this.moveUp = function() {
  setInterval(function() {
    this.yPos--;
    this.domObj.style.top = (this.yPos * multiplicator) + 'px';
  }.bind(this) , 1000);
}
Sushanth --
  • 53,795
  • 7
  • 57
  • 95
  • 1
    You have a gold badge in JavaScript; certainly you know that this is a duplicate of many questions? – Heretic Monkey Feb 13 '17 at 18:40
  • Assuming you don't need IE 8 and below support, is there any reason *not* to use `.bind()` in this instance? – Joseph Marikle Feb 13 '17 at 18:41
  • @MikeMcCaughan Most of the solutions are pretty generic as a whole and it requires to grasp the entire content before understanding it. Thought giving out a minimalistic example would help out the OP as he is new to JS. – Sushanth -- Feb 13 '17 at 18:44
  • @JosephMarikle `.bind` is the way to go as you can embrace the true intent of using `this` context and understanding it instead of resorting to using `lexical scope` which tries to avoid it. – Sushanth -- Feb 13 '17 at 18:45
2

You need to bind the setInterval to the same target as your class.

this.moveUp = function ()
{
    setInterval(function ()
    {
        this.yPos--;
        this.domObj.style.top = (this.yPos * multiplicator) + 'px';
    }.bind(this), 1000);
}
Ribs
  • 1,429
  • 2
  • 13
  • 26