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.
Related
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/
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.
hello I'm just started to learn dojo, how can I use this object? I've created something like below but I thing its not right
var node = dojo.query('.verticalslider')[0];
dojo.connect(node, "onclick", function(){
var c = dojo.query(this).parent();
console.log(c);
})
Fixed code:
// eventlistener is setup on every DOM node with className 'verticalslider'
dojo.query('.verticalslider').connect("click", function(){
// this references the clicked DOM node
var c = this.parentNode
// parentNode of the clicked DOM node with class 'vertical..'
console.log(c);
})
This is more of a general js question then it is a dojo but for the .connect and .on functions following applies:
dojo.connect is a wrapper for creating eventlistener. normally if you write code like node.foo = function() {} you can only have the one function, as equal sign overrides the existing one. The standard behavior of .connect is that the same scope applies, so 'this' is referencing the object we're listening on. In this case 'node'.
dj.connect(node, "foo", function() { this == node evaluates to true and arguments[0] == event });
dojo.hitch (dojo/_base/lang) is a scope attach helper. It works for any event but a timeout/interval hook and will force the function object passed to, say .connect, to run in the given scope as such: dojo.hitch(scope, functor).
dj.connect(node, "bar", dj.hitch(dojo.doc(), function() { this == window.document evals true }));
As far as dojo.query goes, it will return you with a NodeList. A list cannot have a single parent, so your dojo.query(node).parent() is wrong. the correct use of .query is to pass a selector as your first use of it. Like so:
dj.query(
/* String */ "CSS Selector",
/* Optional DOM node, defaults to body */ contextNode
) // => dojo.NodeList
see NodeList docs
The above code mentioned is a straight way through, but if you need the context of this inside any function/ callback , use dojo.hitch (<1.7) or lang.hitch (1.7+). It passes the context of this inside the function.
For Ex:
var myObj = {
foo: "bar"
};
var func = dojo.hitch(myObj, function(){
console.log(this.foo);
});
Here this inside the function refers to the context of the object myObj.
Another Fixed code for you can be:
var node = dojo.query('.verticalslider')[0];
dojo.connect(node, "onclick", dojo.hitch(this,function(){
var c = dojo.query(this).parent(); // here this will be having the outside context .
console.log(c);
}))
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);
});
//...
};