Nick
Nick

Reputation: 674

how to pass this keyword to javascript class functions

I'm running into the undefinded error when trying to access the this.numberObject from another function init.

I'm pretty sure it's because my this keyword is referencing the window object.

My problem is that I can't figure out an elegant way to structure my code.

Current Code:

class IncrementNumbers {
  constructor() {
    this.numberObject = document.querySelector(".incrementNum");
    if (this.numberObject != null) {
      console.log(this.numberObject.innerHTML);
      this.num = this.numberObject.innerHTML;
      this.events();
    }
  }

  events() {
    console.log("events function ran");
    this.init();
  }

  init() {
    let target = 345;
    let current = 0;
    function addOneToElement() {
      if (current === target) {
        clearInterval(addOne);
      }
      current++;
      this.numberObject.innerHTML = current; <---- ERROR HERE
    }
    let addOne = setInterval(addOneToElement, 10);
  }
}

export default IncrementNumbers;

Error:

IncrementNumbers.js:44 Uncaught TypeError: Cannot set property 'innerHTML' of undefined

I can fix the error by adding in

  this.numberObject = document.querySelector(".incrementNum");

to the following function

init() {
    let target = 345;
    let current = 0;
    function addOneToElement() {
      this.numberObject = document.querySelector(".incrementNum");  <--- ADDED HERE
      if (current === target) {
        clearInterval(addOne);
      }
      current++;
      this.numberObject.innerHTML = current;
    }
    let addOne = setInterval(addOneToElement, 10);
  }

However this feels a bit redundant because I'm already referencing the element Object in my constructor. I'd like the function, init to run once the page has loaded.

Is there a better way?

Thank you

Upvotes: 0

Views: 51

Answers (2)

code-gorilla
code-gorilla

Reputation: 2408

The problem is the inner function addOneToElement that creates its own this context within init.

A simple fix would be to use arrow functions which don't have a this context:

class IncrementNumbers {
  init() {
    let target = 345;
    let current = 0;
    // Use an arrow function here.
    const addOneToElement = () => {
      if (current === target) {
        clearInterval(addOne);
      }
      current++;
      this.numberObject.innerHTML = current; <---- ERROR HERE
    }
    let addOne = setInterval(addOneToElement, 10);
  }
}

Another option would be to bind the this context to addOneToElement:

let addOne = setInterval(addOneToElement.bind(this), 10);

Upvotes: 1

Arky
Arky

Reputation: 121

Your problem seems to come from a misspropagation of the this object.
In your function init you set a new function named addOneToElement that is called inside of a setInterval, this setInterval instance does not have access to the this element of your class.

To fix the problem you could try to do something like

class IncrementNumbers {
  constructor() {
    this.numberObject = document.querySelector(".incrementNum");
    if (this.numberObject != null) {
      console.log(this.numberObject.innerHTML);
      this.num = this.numberObject.innerHTML;
      this.events();
    }
  }

  events() {
    console.log("events function ran");
    this.init();
  }

  init() {
    let target = 345;
    let current = 0;
    function addOneToElement() {
      if (current === target) {
        clearInterval(addOne);
      }
      current++;
      this.numberObject.innerHTML = current; // <---- ERROR HERE
    }
    let addOne = setInterval(addOneToElement.bind(this), 10);
  }
}

export default IncrementNumbers;
let addOne = setInterval(addOneToElement.bind(this), 10);

Where you bind the this instance of your class to the function addOneToElement.

Upvotes: 1

Related Questions