13

I think this is a scope problem, but I'm not sure how to fix this. Here is my code: http://jsfiddle.net/9k9Pe/1498/

class FrameCreator{

    constructor(){
        this.createFrame();
    }
    createFrame(){
      var iframe = document.createElement('iframe');
      this.iframe = iframe;
      var frameLoaded=this.frameLoaded;
      iframe.onload = function () {
                    frameLoaded();
      };
      document.body.appendChild(iframe);
    }
    frameLoaded(){
            console.log("frame loaded");
    }
}

class CustomFrameCreator extends FrameCreator{
    addContent(){
            console.log(this); // returns the object
    }
    frameLoaded(){
            console.log(this); // returns undefined
    }
}

var frame=new CustomFrameCreator();
frame.addContent();

frameLoaded() prints undefined, while addContent prints the object.

How to fix this, so I can have a reference in this when frame is loaded?

Thanks

Doua Beri
  • 10,612
  • 18
  • 89
  • 138

4 Answers4

15

Another alternative to .bind() is to use ES6 Arrow function to preserve context:

iframe.onload = () => {
  this.frameLoaded();
};

class FrameCreator {
  constructor() {
    this.createFrame();
  }
  
  createFrame() {
    var iframe = document.createElement('iframe');
    this.iframe = iframe;
    
    iframe.onload = () => {
      this.frameLoaded();
    };

    document.body.appendChild(iframe);
  }
  
  frameLoaded() {
    console.log("frame loaded");
  }
}

class CustomFrameCreator extends FrameCreator {
  addContent() {
    console.log(this); // returns the object
  }
  frameLoaded() {
    console.log(this); // returns the object now
  }
}

var frame = new CustomFrameCreator();
frame.addContent();
Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
  • This creates a function that calls the function. I suspect it is not as efficient as bind. Not sure though. – frodeborli Aug 23 '17 at 18:31
  • @frodeborli `bind()` wraps the function internally, anyway, this is how you would implement the polyfill. The part that makes it less efficient is actually the scoped reference to `this`, which kills some JavaScript optimization potential on the function scope, so you're partially correct, but for the wrong reason. – Patrick Roberts Aug 23 '17 at 18:37
7

Use bind function to bind context

this.frameLoaded.bind(this);

See fiddle

Max Brodin
  • 3,903
  • 1
  • 14
  • 23
1

You need to bind the handler for onload

iframe.onload = function () {
  this.frameLoaded();
}.bind(this);
Daniel A. White
  • 187,200
  • 47
  • 362
  • 445
0

Since you're creating a new scope, this within the onload callback does not reference your class, it instead references the callback function itself. You must bind your frameLoaded method to the proper thisArg.

class FrameCreator {

  constructor() {
    this.createFrame();
  }
  createFrame() {
    var iframe = document.createElement('iframe');
    this.iframe = iframe;
    var frameLoaded = this.frameLoaded;
    iframe.onload = frameLoaded.bind(this)
    document.body.appendChild(iframe);
  }
  frameLoaded() {
    console.log("frame loaded");
  }
}

class CustomFrameCreator extends FrameCreator {
  addContent() {
    console.log(this); // returns the object
  }
  frameLoaded() {
    console.log(this); // returns undefined
  }
}

var frame = new CustomFrameCreator();
frame.addContent();
Seth
  • 10,198
  • 10
  • 45
  • 68