Javascript, Calling functions from another onclick in object constructor - javascript

I need help calling the this.load function from inside the thediv.onclick. I stripped out most of the code so its really basic, but I really can't find a way to do it. Here is what I currently have:
function CreatePizza(Name, Toppings) {
this.n = Name;
this.t = Toppings;
this.load = function loadpizza() {
//function that i want to be called
}
this.create = function button() {
var thediv = document.createElement("div");
thediv.onclick = function() {
// Call this.load function here
}
}
}

The problem is that inside the onclick handler, this will refer to the <div>, not the other this which you refer to repeatedly.
Two possible solutions:
Save a reference to your desired this:
that = this;
thediv.onclick = function () {
that.load()
};
Bind this to your function:
thediv.onclick = function () {
this.load();
}.bind(this);
Or, if that's the only thing you're doing in that function anyway:
thediv.onclick = this.load.bind(this);

Because of closures, you can simply assign this to a variable and call it from that!
function CreatePizza(Name, Toppings) {
var self = this;
this.n = Name;
this.t = Toppings;
this.load = function loadpizza() {
//function that i want to be called
}
this.create = function button() {
var thediv = document.createElement("div");
thediv.onclick = function() {
self.load();
}
}
}
I would like to mention that a nicer - and not necessarily better, before anyone starts a flamewar - way to attach events to your div (and more elegant in my opinion) is to use thediv.addEventListener('click', self.load, false). Just a side-note, though.

Backup the this object before binding the event.
this.create = function button() {
var that = this,
thediv = document.createElement("div");
thediv.onclick = function() {
// Call this.load function here
that.load();
}
}

function CreatePizza(Name, Toppings) {
this.n = Name;
this.t = Toppings;
var foo = function loadpizza() {
//function that i want to be called
};
this.load = foo;
this.create = function button() {
var thediv = document.createElement("div");
thediv.onclick = function() {
foo();
}
}
}

function CreatePizza(Name, Toppings) {
this.n = Name;
this.t = Toppings;
this.load = function loadpizza() {
//function that i want to be called
}
var self = this;
this.create = function button() {
var thediv = document.createElement("div");
thediv.onclick = function() {
self.load()
}
}
}

Related

Javascript inheritance with eventListener

I have a class designMain which extends design.
I have found that if I set an eventListener in design, when the event is fired, all references to this will only refer to the non extended design object. Is there a way to overcome this shortcoming of my extended class?
UPDATE - I've included an ES6 version which works as expected and shows what I am trying to accomplish - namely that this with extended classes will always refer to the extended class even with code (e.g. addEventListener) within the base class. In this case this.name should always be from the extended class.
Here is a jsFiddle
HTML
<div id="test">
Design (click will fail)
</div>
<div id="test2">
DesignMain (click will succeed)
</div>
javascript
design = (function() {
function design() {
this.name = "design";
var _this = this;
var e = document.getElementById("test");
e.addEventListener("click", function() {
_this.callHello();
});
}
design.prototype.callHello = function() {
// I expect that this.name will be "designMain"
console.log(this.name)
// will fail if called from the design class eventListener
this.hello();
}
return design;
})();
designMain = (function() {
function designMain() {
this.name = "designMain";
this.init();
}
designMain.prototype.init = function() {
this.extend();
var _this = this;
var e = document.getElementById("test2");
e.addEventListener("click", function() {
_this.callHello();
});
}
designMain.prototype.extend = function() {
var old = new design();
// save reference to original methods
this._designMain = Object.create(old);
for (var name in this._designMain) {
if (!this[name]) {
this[name] = this._designMain[name]
}
}
}
designMain.prototype.hello = function() {
alert("Hello " + this.name);
}
return designMain;
})();
var t = new designMain();
Using ES6 - it works as expected (see fiddle)
class design {
constructor() {
this.name = "design";
var _this = this;
var e = document.getElementById("test");
e.addEventListener("click", function() {
_this.callHello();
});
};
callHello() {
// should be "designMain"
console.log(this.name)
this.hello();
}
get name() {
return this._name;
};
set name(name) {
this._name = name;
};
}
class designMain extends design {
constructor() {
super();
this.name = "designMain";
var _this = this;
var e = document.getElementById("test2");
e.addEventListener("click", function() {
_this.callHello();
});
};
hello() {
alert("Hello " + this.name);
}
}
t = new designMain();
Thanks to Bergi and the link he posted - I have a solution. Extending / inheriting was way simpler using Object.create and I only needed to add design.call(this) to run the constructor of the base class.
designMain = (function() {
designMain.prototype = Object.create(design.prototype);
function designMain() {
design.call(this)
Working solution:
design = (function() {
function design() {
this.name = "design";
var _this = this;
var e = document.getElementById("test");
e.addEventListener("click", function() {
_this.callHello();
});
}
design.prototype.callHello = function() {
// will fail if called from the design class eventListener
console.log(this.name)
this.hello();
}
return design;
})();
designMain = (function() {
designMain.prototype = Object.create(design.prototype);
function designMain() {
design.call(this)
this.name = "designMain";
this.init();
}
designMain.prototype.init = function() {
var _this = this;
var e = document.getElementById("test2");
e.addEventListener("click", function() {
_this.callHello();
});
}
designMain.prototype.hello = function() {
alert("Hello " + this.name);
}
return designMain;
})();
var t = new designMain();

Object.assign() a deep object

I have a base object ProfileDialog which I am extending with Object.assign().
var ProfileDialog = function (containerObj) {
this.init = function () {
this.container = containerObj;
};
this.render = function () {
let content = document.createElement('div');
content.innerText = 'Dialog here';
this.container.appendChild(content);
};
this.init();
this.render();
};
Mixin:
var DialogMixin = function () {
return {
open: function () {
this.container.style.display = 'block';
},
close: function () {
this.container.style.display = 'none';
}
}
};
Now I do the assignment:
Object.assign(ProfileDialog.prototype, DialogMixin());
It works just fine, this context resolves fine in open and close methods.
But, when I put the mixin in a deeper structure, putting it inside actions property:
var DialogMixin = function () {
return {
actions: {
open: function () {
this.container.style.display = 'block';
},
close: function () {
this.container.style.display = 'none';
}
}
}
};
The context becomes actions object so the code breaks.
How do I properly extend the object with new methods when they are put in a deep structure?
The only thing i can think of is using bind to bind this.
So something like
var ProfileDialog = function (containerObj) {
this.containerObj = containerObj;
};
var DialogMixin = function (newThis) {
var obj = {
actions: {
open: function () {
console.log('open', this, this.containerObj.style);
}
}
}
obj.actions.open = obj.actions.open.bind(newThis);
return obj;
};
var container = {
style : 'some style'
};
var dialog = new ProfileDialog(container);
var mixinDialog = Object.assign(dialog, DialogMixin(dialog));
mixinDialog.actions.open();
See https://jsfiddle.net/zqt1me9d/4/

Is it possible to unit-test this javascript structure?

Given the following JavaScript structure:
addClickEvent: function() {
element.addEventListener('click', function() {
self.a();
self.b();
});
},
Is it possible to assert that a() and b() have been called without refactoring out the anonymous function or editing it's contents?
Assuming the self in your code is the window.self property.
You could do something like this:
function element_onclick_callsAandB() {
// Arrange
var aCalled = false;
var bCalled = false;
var element = ...;
var origA = self.a;
var origB = self.b;
self.a = function() {
aCalled = true;
origA();
};
self.b = function() {
bCalled = true;
origB();
};
try {
// Act
element.click();
// Assert
assertTrue(aCalled);
assertTrue(bCalled);
}
finally {
self.a = origA;
self.b = origB;
}
}

How do I call a Javascript function outside of another Javascript function?

I have a javascript function that's supposed to toggle an animation when clicked by calling another function outside of it.
function MyFunction(id) {
var target = document.getElementById(id);
var on = true;
this.startMe = function() {
//animation code
on = true;
}
this.stopMe = function() {
//animation code
on = false;
}
this.toggleMe = function() {
if (on) this.stopMe();
else this.startMe();
}
target.addEventListener('click', function() {
this.toggleMe();
}, false);
}
The problem lies in the toggleMe and addEventListener functions. "this" refers to the function itself and not the one containing it, which is what I need it to reference. How can I work around this?
The easy fix is to use a closure variable as given below
function MyFunction(id) {
var self = this;
var target = document.getElementById(id);
var on = true;
this.startMe = function () {
//animation code
on = true;
}
this.stopMe = function () {
/animation code
on = false;
}
this.toggleMe = function() {
if (on) this.stopMe();
else this.startMe();
}
target.addEventListener('click', function() {
//this refers to the element here not the instance of MyFunction
//use a closure variable
self.toggleMe();
}, false);
}
Another solution is to pass a custom execution context to the callback using $.proxy() - you can use Function.bind() also but not supported in IE < 9
function MyFunction(id) {
var target = document.getElementById(id);
var on = true;
this.startMe = function () {
//animation code
on = true;
}
this.stopMe = function () {
//animation code
on = false;
}
this.toggleMe = function () {
if (on) this.stopMe();
else this.startMe();
}
//use Function.bind() to pass a custom execution context to
target.addEventListener('click', jQuery.proxy(function () {
// this refers to the element here not the instance of MyFunction
//use a closure variable
this.toggleMe();
}, this), false);
}
Also use .click()/on('click') to register the click handler instead of addEventListener
$(target).on('click', jQuery.proxy(function () {
// this refers to the element here not the instance of MyFunction
//use a closure variable
this.toggleMe();
}, this), false);
Simply add another variable with a reference to this but with a different name; then you can use that in your functions.
function MyFunction(id) {
var self = this;
var target = document.getElementById(id);
var on = true;
this.startMe = function() {
on = true;
}
this.stopMe = function() {
on = false;
}
this.toggleMe = function() {
if (on) self.stopMe();
else self.startMe();
}
target.addEventListener('click', function() {
self.toggleMe();
}, false);
}
My personal preference is to take it even one step further and continue to use self everywhere that makes sense:
function MyFunction(id) {
var self = this;
var target = document.getElementById(id);
var on = true;
self.startMe = function() {
on = true;
}
self.stopMe = function() {
on = false;
}
self.toggleMe = function() {
if (on) self.stopMe();
else self.startMe();
}
target.addEventListener('click', function() {
self.toggleMe();
}, false);
}

"This" not refering to current object

I am kind of new to OOP in JS. I would like to know why when creating sub-objects, this stops referring to the main object AFTER the second level of subobjects.
function Clase()
{
this.__construct = function()
{
this.paginator();
alert('__construct finished');
};
this.paginator = function()
{
this.paginator.title = function()
{
this.paginator.title.set_offsets = function()
{
alert('paginator.title.set_offsets executed!');
};
};
this.paginator.title(); //instantiating
alert('subobject paginator created');
};
this.__construct();
}
var instancia = new Clase();
instancia.paginator.title.set_offsets();
http://jsfiddle.net/WYWwE/
The error is: this.paginator is undefined.
And now, if I use closures, it works perfectly:
function Clase()
{
self = this;
this.__construct = function()
{
this.paginator();
alert('__construct finished');
};
this.paginator = function()
{
self.paginator.title = function()
{
self.paginator.title.set_offsets = function()
{
alert('instancia.paginator.title.set_offsets() executed');
};
};
self.paginator.title();
alert('this.paginator created');
};
this.__construct();
}
var instancia = new Clase();
instancia.paginator.title.set_offsets();
http://jsfiddle.net/esjHu/
So, AFAIK after some point, "this" stops refering to the class "Clase" and refers to something else. If so, is it a good practice to use closures this way?
Is it also correct to start the class with self = this; and from then on use ONLY "self"? for instance: http://jsfiddle.net/byGRX/
You lose the reference to the "original" this when you nest functions. To remedy do the following:
function Clase() {
var that = this;
this.paginator = {
title: {
set_offsets: function() {
alert('paginator.title.set_offsets executed!');
}
}
};
};
var foo = new Clase();
foo.paginator.title.set_offsets();​
http://jsfiddle.net/vd5YK/
You don't lose reference to the this object, here's what happens:
For example:
function Class() {
this.func1 = function () {
this.func1.func2 = function () {
alert('Works!');
};
};
this.func1.func2();
}
x = new Class();
Now, the reason you get an error saying that func2 doesn't exist is because the function object for func2 isn't constructed until you call func1:
function Class() {
this.func1 = function () {
this.func1.func2 = function () {
alert('Works!');
};
};
this.func1();
this.func1.func2();
}
x = new Class();
And now it works.
EDIT:
So, why doesn't this work:
function Class() {
this.func1 = function() {
this.func1.func2 = function() {
this.func1.func2.func3 = function() {
alert('works!');
};
this.func1.func2.property = 5;
};
};
this.func1();
this.func1.func2();
}
x = new Class();
x.func1.func2.func3();
Basically, what your trying to do is add a property named property and a method named func3 to the function object of func2, but the problem is that func2 isn't constructed before calling func1. It's the same as doing:
function Class() {
this.func1 = function() {
this.func1.func2 = function() {};
};
this.func1.func2.func3 = function() {
alert('works!');
};
this.func1.func2.property = 5;
this.func1();
this.func1.func2();
}
x = new Class();
x.func1.func2.func3();
If you want it to work you need first construct the function object for func2 by calling func1:
function Class() {
this.func1 = function() {
this.func1.func2 = function() {};
};
this.func1();
this.func1.func2.func3 = function() {
alert('works!');
};
this.func1.func2.property = 5;
// this.func1.func2();
}
x = new Class();
x.func1.func2.func3();
alert(x.func1.func2.property);

Categories

Resources