Usually I develop on Java, and now I am studying JavaScript/HTML5 Canvas things. And I get a strange situation from Java developer's point of view.
There's a html5 canvas object on the html page, and I want to track the mouse click events on this canvas.
I declared class GameBoard and initialized its properties:
function GameBoard() {
// defining a property for GameBoard class instance
this.myProperty = 'some text here';
// getting canvas element
this.boardCanvas = document.getElementById("myCanvas");
// setting the mouse click event listener
this.boardCanvas.addEventListener("mousedown", this.handleMouseClick, false);
}
and there's a class method to handle mouse click events:
GameBoard.prototype.handleMouseClick = function(event) {
alert(this.myProperty);
}
handleMouseClick() will display undefined because this in handleMouseClick() method refers to the HTML5 Canvas instance (boardCanvas).
My question: how can I refer the current GameBoard class instance inside of handleMouseClick method to get myProperty field value defined in the class constructor?
What I am doing wrong here?
Thank you.
One of the common conventions is to use an alias to this, usually with a variable named self:
function GameBoard() {
// defining alias
var self = this;
this.myProperty = 'some text here';
this.boardCanvas = document.getElementById("myCanvas");
this.handleMouseClick = function()
{
// using alias
alert(self.myProperty);
};
this.boardCanvas.addEventListener("mousedown", this.handleMouseClick, false);
}
However, since you're defining the method on the prototype here, you can either use bind (as proposed by #Alexander) or try this:
var self = this;
this.boardCanvas.addEventListener("mousedown", function(e)
{
// calling the function with 'self/this' context
self.handleMouseClick(e);
}, false);
(Thanks to #Alexander for his contributions)
You can use bind in order to set this for function
this.boardCanvas.addEventListener("mousedown", this.handleMouseClick.bind(this), false);
Example: http://jsbin.com/vutugi/1/
Related
I have two functions that I use as classes: Person and Eye.
Person creates an Eye object in itself. Later on I try to access the eye object created, with an event handler for whenever the user clicks on the web page.
function Eye(){
this.color="Green";
}
function Person() {
this.name="John";
this.eye = new Eye();
//eye=this.eye; // uncomment and everything works!
document.addEventListener("click", function(){
console.log(name); // This works
console.log(eye); // This doesn't work
})
}
var person= new Person();
Why doesn't this work? Making a second variable eye seems to solve the issue but I have no clue why..
It doesn't work because "eye" is not a variable, it's a property of an object. You're doing nothing to tell JavaScript what object to look at.
You'll have to save the value of this in another local variable:
function Person() {
this.name="John";
this.eye = new Eye();
var person = this;
Then you can use that in the event handler:
console.log(person.eye);
document.addEventListener("click", function() {
console.log(name);
console.log(eye);
});
In this context, name refers to the name property of the window object, since you didn't specify the object off of which you wanted to access it. window.name just so happens to return "result" on my implementation. And ogging eye won't work because eye has not been defined as a variable.
To fix this, use a variable to store the reference to the current object outside the event function and use it inside.
var ref = this;
document.addEventListener("click", function() {
console.log(ref.name);
console.log(ref.eye);
});
Output:
John
Eye { color: "Green" }
Live Demo
Being a long time C++/C# developer, I find myself moving a lot of of my JS code into "classes" to group functions and data together. As those classes handle event though, I'm finding myself having to write "stub" handlers that serve only to route the calls into a class method to provide the proper this context. So I'm doing things like this:
var Manager = {
foo: 'x',
bar: 1,
onClickStub: function(evt) {
// 'this' refers to HTMLElement event source
Manager.onClick(evt);
},
onClick: function(evt) {
// 'this' now refers to Manager.
// real work goes here.
}
}
Is this the normal way of doing things or is there a better way to structure my event handlers while keeping my class organization?
As Joseph Silber said in the comments above, I think bind would be perfect in this case. If you need to support older browsers, you can always add a shim to Function.prototype.bind (see an example implementation in the MDN docs). Then your code could just be:
var Manager = {
var foo: 'x',
var bar: 1,
// no more stub!
onClick: function(evt) {
// 'this' will refer to Manager.
// real work goes here.
}
}
// And when you bind the event handler:
var el = document.getElementById('something');
el.addEventListener('click', Manager.onClick.bind(Manager));
The best way that I know to do this is to assign this to another variable at the top of your class and then refer to that one throughout the class. But of course this only works if you are not using an anonymous object as your class.
For instance:
var Manager = function(){
var self = this,
var foo = 'x',
var bar = 1;
var onClick = function(evt) {
console.log(self); // refers to the manager
console.log(this); // refers to the element the onclick is assigned to
// If you want this to equal the manager then just do: this = self;
}
}() // edit: to make this an immediate function
In response to a comment below you could attach the onclick like this
element.onclick = Manager.onClick;
Then in this case the this variable in the onclick function is indeed the html element, and the self variable is the Manager function.
We can modify a DOM element and add to its prototype. For example, if we want to add something only to the canvas, we'd do something like this:
HTMLCanvasElement.prototype.doSomething = function(arg) { ... };
We can then perform this action on a canvas element:
var canvas = document.getElementById('canvasId');
canvas.doSomething(...);
Is it possible to add/attach a function to this instance of the canvas without modifying the prototype of HTMLCanvasElement. I only want a canvas where doSomething(...) was called to have access to the additional methods, not all canvas elements in the DOM. How can I do this?
I've tried the following in my doSomething function:
this.prototype.foobar = function() {...}
However, prototype is undefined here.
Shusl helped me come up with the correct answer. It was easier than I thought. In my doSomething(args) function, instead of trying to modify the object prototype, I just directly attached the function. Here's the full source code:
HTMLCanvasElement.prototype.doSomething = function(args) {
this.foobar = function(args) { ... };
}
var canvas = document.getElementById('canvasId');
canvas.doSomething(...);
canvas.foobar(...);
Now, foobar is only accessible to the instance of the canvas where doSomething was called. At the same time, I don't have to have any information about the instance.
In that case you can directly attache a method to your canvas object
var canvas = document.getElementById('canvasId');
canvas.doSomething= function() {...}; ///doSomething will only be available to this particular canvas.
canvas.doSomething(...);
With jQuery, you can use the data property.
//setting the function
$('element').data('doSomething', function(arg) { ... });
//calling the function
$('element').data('doSomething')(arg);
JSFiddle
Object.defineProperty(element, 'doSomething', {value:function(arg){ ... }} );
Where 'element' is the element you want to add the property to,
'doSomething' is the name and the
third argument is an object of the property its self. In your case a function.
For example:
var mycanvas = document.createElement("canvas");
Object.defineProperty(mycanvas, 'doSomething', {
value: function(x){console.log(x); },
configurable: true
});
mycanvas.doSomething('my message');
//prints 'my message' to the console.
The 'configurable' property specifies if you would like the 'doSomething' property to be able to be changed again after it is created. Check out the MDN Details for more information and examples.
I'm now trying to register a mouse event listener on a canvas element.
As my JS application object initializes, it binds mouse events to a custom "MouseController" class:
this.canvas.addEventListener('mousedown',this.input0.btnDown, false);
"this" is the application object being initialized and canvas is a reference to the basic HTML5 canvas on my page.
The controller is a simple class like:
function MouseController(eventHandler) {
this.handler = eventHandler;
this.contact = false;
this.coordX = 0;
this.coordY = 0;
}
MouseController.prototype.btnDown = function(event) {
// Faulty line ???
this.updateCoordinates(event);
}
MouseController.prototype.updateHIDCoordinates = function (event) {
// code to set coordX and coordY of the controller instance
// Again, I can't access the object from here as "this" refers to the Canvas
}
As the mouse is clicked, the console logs "result of expression this.updateCoordinates' is not a function". A previous discussion taught me about the reference "this" being lost, ending up being bound to, in this case, the Canvas item.
So I'm looking for a way to call "btnDown" like I would in java, meaning that it executes as part of an object and thus has access to the variables of that object.
The only solution I found was a singleton... No good :( I'm sure there's a clean solution...
Please help :-)
Thanks!
J.
Either create a closure:
var controller = this.input0;
this.canvas.addEventListener('mousedown', function(event){
controller.btnDown(event);
}, false);
or use ECMAScript 5's .bind() [MDN]:
this.canvas.addEventListener('mousedown', this.input0.btnDown.bind(this.input0), false);
Also note that your other method is called updateHIDCoordinates, not updateCoordinates.
I have an object I created in JavaScript. Let's say it looks like this:
function MyObject() {
this.secretIdea = "My Secret Idea!";
};
MyObject.prototype.load = function() {
this.MyButton = $(document.createElement("a"));
this.MyButton.addClass("CoolButtonClass");
this.MyButton.click = MyButton.onButtonClick;
someRandomHtmlObject.append(this.MyButton);
};
MyObject.prototype.onButtonClick = function(e) {
alert(this.secretIdea);
};
As you can see, I have an object setup in JavaScript and, when it's loaded, it creates an anchor tag. This anchor tag as a background image in CSS (so it's not empty).
Now, I understand that the 'this' statement, when the button would actually be clicked, would fall to the scope of the MyButton element rather than the object I have created.
I have tried using call(), try() and bind() and I cannot get this to work. I need it so that, when the button is clicked, it goes back to the object's scope and not the html element's scope.
What am I missing here?
The this value inside the click event handler refers to the DOM element, not to the object instance of your constructor.
You need to persist that value, also you refer to MyButton.onButtonClick I think you want to refer the onButtonClick method declared on MyObject.prototype:
MyObject.prototype.load = function() {
var instance = this;
//..
this.MyButton.click(function (e) { // <-- looks like you are using jQuery
// here `this` refers to `MyButton`
instance.onButtonClick(e);
});
//...
};