1

I played around with the html5 imports and JS to create some kind of mvc and now I have a problem.

When I import a template and append its elements to my DOM and register an event it is fired and all is good. But then the this instance holds the importet template instead of the object which's property function the binding method is.... :)

Important lines for that issue - to understand the process - have comments near by ....

My question is: How can I alocate the parent object of that method to my self variable?

I have some controller parent object:

var controller = function(){
};
controller.prototype.import = function(filepath, callback){

    var link = document.createElement('link');
    link.rel = 'import';
    link.href = filepath;
//important line follows
    link.onload = callback;
    link.setAttribute("async", '');
    document.head.appendChild(link);

    return link.import;
};

controller.prototype.display = function(content, selector, target){

    var new_element = content.querySelector(selector);
     document.querySelector(target).appendChild(new_element.cloneNode(true));
};

And some xyz-controller extending controller (here is the problem):

var xyz_controller = function(){
    //behold ... here the callback is defined    
    this.import('/templates/navigation.html', this.init_navigation);
};
xyz_controller.extends(controller);

xyz_controller.prototype.init_navigation = function(){
    //if I console.log(this) it holds the imported template and not xyz_controller??
    var self = this;

    $('#goto-settings').on('click', function(e){
        e.preventDefault();
        // calls some method I removed for the example, but here was a problem - I sadly cannot call that function 
        self.settings("param");

        return false;
    });
};

The navigation puts the dom elements to the parent document by it selfe like this (this is IN the template file in the end):

<script>
    xyz.display(document.currentScript.ownerDocument,'#main-navigation', '#Header');
</script>

And there is also some main.JS, which does something with jquery and so:

//to inherit all methods from an object
Function.prototype.extends = function(object){
    for(var property in object.prototype){
        this.prototype[property] = object.prototype[property];
    }
};

var xyz;
$( document ).ready(function(){
    xyz = new xyz_controller();
});
Dan Prince
  • 27,111
  • 12
  • 81
  • 112
helle
  • 10,103
  • 8
  • 51
  • 81

1 Answers1

3

In this line:

link.onload = callback;

you assign a function reference. Function references normally do not impose what this will be when the function is called. That is only determined when the function is called.

When a function is called, the value for this corresponds to the object you call that function on (I oversimplify, see this on MDN). In this case it is the DOM (of the imported document) that calls the callback function, and so that document determines the value of this.

Even if you would have assigned it more directly like this, it would still not use your object for this:

link.onload = this.init_navigation;

To avoid that, bind the callback function explicitly to this; that will overrule the above described behaviour:

link.onload = callback.bind(this);

You might find "How does the “this” keyword work?" an interesting read.

And here is a snippet that illustrates how the this keyword behaves differently, depending on whether you use bind or not:

var msg = 'I am a window variable<br>';
var test = {
     msg: 'I am a test variable<br>',
     whenLoad: function () {
         document.body.innerHTML += this.msg;
     },
};


window.addEventListener("load",  test.whenLoad); // *this* will be window
window.addEventListener("load",  test.whenLoad.bind(test)); // *this* will be test
Community
  • 1
  • 1
trincot
  • 211,288
  • 25
  • 175
  • 211