dojo using this - javascript

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);
}))

Related

How adding event handler inside a class with a class-method as the callback?

How do I add an event handler inside a class with a class-method as the callback?
<div id="test">move over here</div>
<script>
oClass = new CClass();
function CClass()
{
this.m_s = "hello :-/";
this.OnEvent = OnEvent;
with(this)
{
var r = document.getElementById("test");
r.addEventListener('mouseover', this.OnEvent); // this does NOT work :-/
}
function OnEvent()
{
alert(this); // this will be the HTML div-element
alert(this.m_s); // will be undefined :-()
}
}
</script>
Yes I know some quirks to make it work but what would be the intended way when these event handlers were introduced ??? I again have the bitter feeling, that no-one truly lives OOP :-(
Here for you to play: https://jsfiddle.net/sepfsvyo/1/
The this inside the event listener callback will be the element that fired the event. If you want the this to be the instance of your class, then either:
Bind the function to the class instance:
Using Function.prototype.bind, will create a new function that its this value will always be what you specify it to be (the class instance):
r.addEventListener('mouseover', this.OnEvent.bind(this));
// ^^^^^^^^^^^
Wrap the function inside an anonymous function:
var that = this;
r.addEventListener('mouseover', function(ev) { that.OnEvent(ev); });
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
or use an arrow function (so no need for that):
r.addEventListener('mouseover', ev => this.OnEvent(ev));
// ^^^^^^^^^^^^^^^^^^^^^^
Note: As mentioned in a comment bellow, both of the above methods pass a different function to addEventListener (the one with bind create a new function, and the anounimous function is obviously !== this.OnEvent). If you are going to remove the event listener later, you'll have to store a reference to the function:
var reference;
r.addEventListener('mouseover', reference = this.OnEvent.bind(this));
// ^^^^^^^^^^^^
or:
var reference;
var that = this;
r.addEventListener('mouseover', reference = function(ev) { that.OnEvent(ev); });
// ^^^^^^^^^^^^
then you can remove the event listener like:
r.removeEventListener('mouseover', reference);
You can actually return the object as an EventListener callback, this way JS will search for an handleEvent method in the class and execute accordingly :
var myInstance = new myClass;
myInstance.addEventListener("mousedown",myInstance);
// To remove the event you can follow the same pattern
myInstance.removeEventListener("mousedown",myInstance);
You have to construct your class this way :
class myClass {
constructor(){
// Whatever this is supposed to do.
// You can also add events listener within the class this way :
this.addEventListener("mousedown",this);
}
mouseDownEvent(e)(){
// Some action related to the mouse down event (e)
console.log(e.target);
}
mouseMoveEvent(e)(){
// Some action related to the mouse move event (e)
}
mouseUpEvent(e)(){
// Some action related to the mouse up event (e)
}
handleEvent(e) {
switch(e.type) {
case "mousedown":
this.mouseDownEvent(e);
break;
case "mousemove":
this.mouseMoveEvent(e);
break;
case "mouseup":
this.mouseUpEvent(e);
break;
}
}
}
Sources :
https://medium.com/#WebReflection/dom-handleevent-a-cross-platform-standard-since-year-2000-5bf17287fd38
https://www.thecssninja.com/javascript/handleevent
https://metafizzy.co/blog/this-in-event-listeners/
I find this method clearer, also while declaring events inside the class this is pretty explicit.
Hope I helped someone.
The answer from #ibrahimmahrir does the job, but I wanted to consolidate a few points.
As many JavaScript developers struggle to understand, the this keyword is a moving target. In traditional OOP languages, an object method is exclusive to the object, so this is defined as the object to which it is attached.
JavaScript functions are more promiscuous, and can be attached to multiple objects. In JavaScript, this refers to the object which is currently invoking the function, not necessarily the one to which it was originally attached.
For an Event Handler function, the invoking object is the element to which it is attached, not the original object; thus this refers to the element. The usual safe method is to store a reference to the original object in a different variable, often called that:
oClass = new CClass();
function CClass() {
var that = this; // a reference to the original object
this.m_s = "hello :-/";
this.OnEvent = OnEvent;
var r = document.getElementById("test");
r.addEventListener('click', this.OnEvent);
function OnEvent() {
alert(that); // this is now the object
alert(that.m_s); // this works
}
}
The comments above are my updated comments. I have also removed the with statement which wasn’t contributing much and which is seriously discouraged.
Oh, and I have changed the event to click to make it easier to test.
While we’re on the confusion with this, it is not necessarily the element which started things off. Suppose we now include a span:
<div id="test">click <span>over</span> here</div>
Clicking on the span will trigger the event listener, even though the you didn’t actually click on the div to which it is attached. In this case the event is bubbled from the span to the div.
Here this refers only to the div element with the event listener. If you want to reference the span, you will need event.target:
function OnEvent(event) { // include event parameter
alert(this); // the element attached
alert(event.target); // the element clicked
alert(that); // this is now the object
alert(that.m_s); // this works
}
Here is an updated fiddle: https://jsfiddle.net/osk083xv/

Best practice for obtaining 'this' context

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.

Applying OOP with jQuery

I'm working with jQuery and trying to apply some basic Javascript OOP principles to a set of functions that control hover behavior. However, I can't figure out how to get the "this" keyword to refer to the instance of the object I'm creating. My sample code is:
var zoomin = new Object();
zoomin = function() {
// Constructor goes here
};
zoomin.prototype = {
hoverOn: function() {
this.hoverReset();
// More logic here using jQuery's $(this)...
},
hoverReset: function() {
// Some logic here.
}
};
// Create new instance of zoomin and apply event handler to matching classes.
var my_zoomin = new zoomin();
$(".some_class").hover(my_zoomin.hoverOn, function() { return null; });
The problematic line in the above code is the call to this.hoverReset() inside the hoverOn() function. Since this now refers to element that was hovered on, it does not work as intended. I would basically like to call the function hoverReset() for that instance of the object (my_zoomin).
Is there any way to do this?
Only assigning a function to a property of an object does not associated this inside the function with the object. It is the way how you call the function.
By calling
.hover(my_zoomin.hoverOn,...)
you are only passing the function. It will not "remember" to which object it belonged. What you can do is to pass an anonymous function and call hoverOn inside:
.hover(function(){ my_zoomin.hoverOn(); },...)
This will make the this inside hoverOn refer to my_zoomin. So the call to this.hoverReset() will work. However, inside hoverOn, you will not have a reference to the jQuery object created by the selector.
One solution would be to pass the selected elements as parameter:
var zoomin = function() {
// Constructor goes here
};
zoomin.prototype = {
hoverOn: function($ele) {
this.hoverReset($ele);
// More logic here using jQuery's $ele...
},
hoverReset: function($ele) {
// Some logic here.
}
};
var my_zoomin = new zoomin();
$(".some_class").hover(function() {
my_zoomin.hoverOn($(this)); // pass $(this) to the method
}, function() {
return null;
});
As a next step, you could consider making a jQuery plugin.
You can "bind" the event handler to the object (see Mootools bind code for example).
You can pass the object as a parameter in the anonymous function and use that instead of this in the event handler
As for 1, you add the bind method to function
bind: function(bind){
var self = this,
args = (arguments.length > 1) ? Array.slice(arguments, 1) : null;
return function(){
if (!args && !arguments.length) return self.call(bind);
if (args && arguments.length) return self.apply(bind, args.concat(Array.from(arguments)));
return self.apply(bind, args || arguments);
};
}
Not sure though how well it will interact with JQ stuff.
please see my answers to these questions:
where is my "this"?
why is "this" not this?
this confusion comes up all the time.
when you pass a function in as a callback, it's invoked as a standalone function, so its "this" becomes the global object.
"bind" is a native part of ecmascript 5, and is part of the function prototype. If you go to the end of my second answer up there, you get a link to the mozilla website, which has a "compatibility" version of the bind function. Use use myfunction.bind(myobject), and it'll use the native function if it's available, or the JS function if it is not.

How do I redefine `this` in Javascript?

I have a function which is a JQuery event handler. Because it is a JQuery event handler, it uses the this variable to refer to the object on which it is invoked (as is normal for that library).
Unfortunately, I need to manually call that method at this point. How do I make this inside the called function behave as if it were called from JQuery?
Example code:
function performAjaxRequest() {
//Function which builds AJAX request in terms of "this"
}
function buildForm(dialogOfForm) {
var inputItem;
dialogOfForm.html('...');
dialogOfForm.dialog('option', 'buttons', {
"Ok" : performAjaxRequest
});
inputItem = dialogOfForm.children(':not(label)');
//Redirect enter to submit the form
inputItem.keypress(function (e) {
if (e.which === 13) {
performAjaxRequest(); //Note that 'this' isn't the dialog box
//as performAjaxRequest expects here, it's
//the input element where the user pressed
//enter!
}
}
}
You can use the function's call method.
someFunction.call(objectToBeThis, argument1, argument2, andSoOnAndSoOn);
If dialog is the object that you need to be set to this then:
performAjaxRequest.apply(dialog, []);
// arguments (instead of []) might be even better
should do the trick.
Otherwise, in jQuery you can simply call the trigger method on the element that you want to have set to this
Say, for example, that you wanted to have a click event happen on a button and you need it to happen now. Simply call:
$("#my_button").trigger("click");
Your #my_button's click handler will be invoked, and this will be set to the #my_button element.
If you need to call a method with a different this ... say for example, with this referring to the jQuery object itself, then you will want to use call or apply on your function.
Chuck and meder have already given you examples of each ... but to have everything all in one place:
// Call
my_function.call(object_to_use_for_this, argument1, argument2, ... argumentN);
// Apply
my_function.apply(object_to_use_for_this, arguments_array);
SEE: A List Apart's Get Out of Binding Situations
Are you looking for..
functionRef.apply( objectContext, arguments);
You should of course learn to master call() and apply() as people have stated but a little helper never hurts...
In jQuery, there is $.proxy. In pure js, you can re-create that niftyness ;) with something like:
function proxyFn( fn , scope ){
return function(){
return fn.apply(scope,arguments);
}
}
Usage Examples:
var myFunctionThatUsesThis = function(A,B){
console.log(this,arguments); // {foo:'bar'},'a','b'
};
// setTimeout or do Ajax call or whatever you suppose loses "this"
var thisTarget = {foo: 'bar'};
setTimeout( proxyFn( myFunctionThatUsesThis, thisTarget) , 1000 , 'a', 'b' );
// or...
var contextForcedCallback = proxyFn( myAjaxCallback , someObjectToBeThis );
performAjaxRequest(myURL, someArgs, contextForcedCallback );
If you dont abuse it, it's a sure-fire tool to never loose the scope of "this".
use a closure
i.e assign this to that early on; then you can do what you like with it.
var that = this;

Handling events on dynamically generated JavaScript objects

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);
});
//...
};

Categories