Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript

Style property is undefined

In my program, I have crafted a bullet formula with CSS. In JavaScript, I want the bullet to go at a certain rate that I pass to my fire() method. After I append my bullet to the document, I have this code:

function shoot() {
  this.bullet.style.left = parseInt(this.bullet.style.left) + bulletSpeed;
}

setInterval(shoot, 20);

This should in theory "shoot" the bullet. However, I get the error "Cannot read style property of undefined", why is this?

Here's my whole program:

window.onload = function() {

let body = document.getElementsByTagName('body')[0]; 

function Position() {

  this.x = 0;
  this.y = 0;
  this.element;
  this.bullet;

  this.appear = function() {

    this.element = document.createElement('img');
    this.element.src = 'https://mail.google.com/mail/u/0/?          ui=2&ik=c6acbee890&view=att&th=164ce1ed771b2f42&attid=0.1&disp=safe&realattid=f_jk06yza50&zw';
    this.element.style.position = 'absolute';
    this.element.style.zIndex = 80;
    this.element.style.left = 8;
    this.element.style.bottom = 10;
    this.element.style.height = '125px';
    this.element.style.width = '160px';
    this.element.className = 'character';
    body.appendChild(this.element);
  }

  this.setPosition = function(x, y) {

     this.x = x;
     this.y = y;
     this.element.style.left += this.x;
     this.element.style.bottom = this.y;
  }

  this.move = function(xspeed, yspeed) {

    document.addEventListener('keydown', (event) => {

    if (event.keyCode == '39') this.element.style.left = parseInt(this.element.style.left) + xspeed;
    if (event.keyCode == '38') this.element.style.bottom = parseInt(this.element.style.bottom) + yspeed;
    if (event.keyCode == '37' && this.element.style.left != 0) this.element.style.left = parseInt(this.element.style.left) - xspeed;
    if (event.keyCode == '40' && this.element.style.bottom != 10) this.element.style.bottom = parseInt(this.element.style.bottom) - yspeed;
  }); 
  }

  this.fire = function(bulletSpeed, bulletColor) {

    document.addEventListener('keydown', (event) => {

    if (event.keyCode == '32') {

    this.bullet = document.createElement('div');
    this.bullet.style.padding = '10px';
    this.bullet.style.position = 'absolute';
    this.bullet.style.left = parseInt(this.element.style.left) + 128;
    this.bullet.style.bottom = parseInt(this.element.style.bottom) + 55;
    this.bullet.style.width = '10px';
    this.bullet.style.backgroundColor = bulletColor;
    body.appendChild(this.bullet);


      function shoot() {
        this.bullet.style.left = parseInt(this.bullet.style.left) + bulletSpeed;   
      }

      setInterval(shoot, 20);

    }

  });
  }
}

let character = new Position();

character.appear();
character.move(8, 10);
character.fire(8, 'blue');
};

1 Answer

Steven Parker
Steven Parker
230,248 Points

When the function "shoot" is called by the interval timer, it no longer has any reference to the object context for "this" to work.

So you might want to define the function to accept the bullet as an argument, and then give that argument to the interval timer to pass on each call:

      function shoot(bullet) {
        bullet.style.left = parseInt(bullet.style.left) + bulletSpeed;   
      }

      setInterval(shoot, 20, this.bullet);

Thank you for helping me out of yet another pickle, Steven. Quick question, could I also do this:

function shoot(bullet) {
bullet.style.left = parseInt(bullet.style.left) + bulletSpeed;
}

setInterval(function() { shoot(this.bullet) }, 20);

?

Steven Parker
Steven Parker
230,248 Points

Putting parentheses (with or with arguments) after a method name calls the method immediately, and passes the return value. But this is a case where you want to pass the method itself as a callback.

That's why the timer method takes additional parameters, they will be passed on when the method is called later.

Ah I see. Thanks for all your help!