"this" in jquery widget callback - javascript

I have the following widget
var a = $('#test').timetable({
cell_click: openDialog
});
whereby cell_click is an event generated by
_create:function(){
dayBodyCells.click(function(){
if( !$(this).hasClass('cell-inactive') ){
var dic = self.getElementPos(this);
self._trigger('cell_click', null,dic);
}
});
and openDialog is the callback function. In the callback function for the dayBodyCells, i have this equaling the td element, which is what i expected. I'm curious - why does this inside function openDialog instead refers to #test?

Within a bound event handler (callback), this refers to the element on which the event was triggered. So:
$('#myid').click(function(){
// this is the #myid element
})
In your code, dayBodyCells must be a td (as you expect) therefore this refers to it in the click handler. However, when you trigger the cell_click event, you must be firing it from the #test element (via self._trigger).
If self._trigger('cell_click', null,dic) were replaced with $(this).trigger('cell_click', null,dic), this would then refer to the td within openDialog
Have a look at http://www.pkshiu.com/loft/archive/2009/01/understanding-this-this-and-event-in-a-jquery-callback-function and http://api.jquery.com/category/events/

'Cause it just does? The this of any function is established at call-time, and the contract of an event-handler is that this is set to the DOM element that had event, not the object in which the event-handler could be found.
Consider the following:
b = "DOG".toLowerCase
console.log(b());
You might think this would print out "dog", but no. toLowerCase prints out the lower-case version of the String that this points to. When a function is called like that, this is set to undefined so you get
TypeError: can't convert undefined to object
(At least one Firefox -- afaik, every browser will fail in some fashion.)
Your confusion might be that this seems vaguely like a lexically-bound variable. It isn't, it's much closer to an ordinary function argument or the arguments list itself.

Related

General way to provide the event argument in a function in JavaScript

I am declaring the event argument for years and years on roughly the same manner:
element.onclick = function(e){};
element.onclick = function(event){};
element.addEventListener("click", function(e){}, false);
element.addEventListener("click", eventHandler, false); //preferred version
function eventHandler(e){}
etc.
Does it have to be e or event.
Is it appended as the last argument or can it also be the first?
Can you do this:
function eventHandler(e, a, b, c, d){}
Is e still referring to the event or is it d?
Or to make the above work you need to explicitly use the keyword event?
So the general question is: What are the rules of thumb when it comes to declaring the event argument within a function in JavaScript?
In the following I assume you are referring to about DOM events handlers.
Does it have to be e or event.
It doesn't matter. It could be ajdlahksjd for all we care.
Is it appended as the last argument or can it also be the first?
The event object will always be passed as first argument to the handler.
Can you do this:
function eventHandler(e, a, b, c, d){}
Is e still referring to the event or is it d?
It depends on how you bind the event handler. If you just assign it it will be e. If you already bind other values to the parameters, e.g. via .bind, it will be the first un-bound parameter. However, that doesn't really have anything to do with event handlers. The event handler itself will also get the event object as first argument.
Or to make the above work you need to explicitly use the keyword event?
event is not a keyword. You can name the parameter however you want to.
What are the rules of thumb when it comes to declaring the event argument within a function in JavaScript?
There is none. There is no magic statically inspection of your event handler. It's simply a function that accepts an argument, that's it.
However, there is certainly a convention to name the parameter e or event, simply because it's reasonable name for a variable that represents an event object.
Personally, I think there's no rule about this, but the great Nicholas Zakas, in his book Maintainable JavaScript, tells that if you want to have a function easy to test and maintain, you never have to declare the event as follows:
function myFunction(event) {}
Instead, you'd have to declare the arguments that the function needs:
function myFunction(clientX, clientY) {}
And when you call the function, you must pass the arguments from the object event:
// This won't work on IE8 and older versions.
element.addEventListener('click', function(event) {
myFunction(event.clientX, event.clientY);
}, false);
When you or another guy from your team have to test this function, s/he doesn't need to emulate any event.

How does the onmouseover attribute know to accept an event object?

I was looking at this basic example below (which makes all images in the DOM semi-transparent on mouseover), and was confused as to how an arbitrary function, such as handleMouseOver, receives an event object if you give it an argument.
How is it that the act of assigning such a function to the onmouseover attribute tells it to modify this function in this way, as there's nothing inherent in the function definition itself that says: "please pass me an event"? Is the assignment operator being overloaded somehow? Or is the browser doing some extra work here? I would really appreciate a link to a detailed explanation of this phenomenon because it doesn't seem to make any sense looking at it as pure JavaScript (to me at least!)
function handleMouseOver(e) {
e.target.style.opacity = 0.5;
}
function handleMouseOut(e) {
e.target.style.opacity = 1;
}
var elements = document.getElementsByTagName("img");
for (var i = 0; i < elements.length; i++) {
elements[i].onmouseover = handleMouseOver;
elements[i].onmouseout = handleMouseOut;
}
Lets break it down by taking one browser's example. IE'S OnMouseOver Event for instance.
In the remarks section it says it passes IHTMLEventObj for ALL events even for the events that don't require it such as Body.OnLoad.
When we go into IHTMLEventObj's detail, we read the following remarks
Although all event properties are available to all event objects, some properties might not have meaningful values during some events
So, Event object is passed regardless; you have to access the object in some specific events and get event-specific properties to get event-related data.
onmouseover, for example, is an event handler. When the event handler needs to be called (in this case when the browser javascript engine decides it) then it will call it be passing it some pre-determined arguments (all good documentation will explain what those arguments are). Your use of those arguments is optional however.
This can be demonstrated with a manual function call like so:
function myFunction(e){
alert(e.myProperty);
}
//assign the handler
var handler = myFunction;
//when required, create event parameter data and call the function assigned to the handler
var myE = { myProperty: "some data" };
handler(myE);
It is not "exactly" how it works (because I don't know how browsers have chosen to implement their code), but it shows the concept.
Here is an example in action
Not only the event object is passed, but also the this value within the function is set to the event target. This is done by the browser, and dictated by the DOM specification.
EDIT:
I was hoping to find something more detailed in the DOM specification (I'm sure I've seen that before), but so far I found this:
In JavaScript, user-defined functions are considered to implement the EventListener interface. Thus the Event object will be provided as the first parameter to the user-defined function when it is invoked. Additionally, JavaScript objects can also implement the EventListener interface when they define a handleEvent method.
https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#glossary-event-handler
By the way, the last sentence is talking about an interesting way to bind event listeners, in an OO context.
You can pass whatever arguments you like to any JavaScript function.
Defining them in the function definition just means you have a named, local variable to access them with.
That is to say:
function foo() {
}
foo("hello");
… won't throw an error.
When a function is treated as an event handler (which is what code provided by the browser will do if you assign a function to the onmouseover property of a DOM node) then the event object will be passed as an argument.

this and $(this) in widget

What do this and $(this) mean inside a widget method like create?
For example, Having a widget like this,
$.widget("sample.CustomWidget", {
options:{
},
_create: function(){
// Here what do this and $(this) mean
}
});
Thanks in advance,
-Raja.
It basically depends on the caller of the _create method... anyway:
this refers to the 'owner' of the Function
$(this) is the above object wrapped into a jQuery object
see also:
http://www.quirksmode.org/js/this.html
http://www.bennadel.com/blog/1838-Wrapping-The-Window-Object-In-A-jQuery-Wrapper.htm
Within an event handler of a standard jQuery UI-style widget, this will refer to the DOM element related to the event, $(this) creates a jQuery wrapper around this, and the DOM element (again) is available as this.
But when your widget functions are called, typically this refers to the jQuery object on which the function was called, not a specific DOM element. You then operate on the matched set of elements within that jQuery object.
In a method integrated with jQuery:
$.fn.yourFunction = function() {
// ...
};
the value of this is the jQuery object itself. There's no reason, therefore, to wrap this with another call to jQuery, though it doesn't hurt.
In a callback to an event handler:
$('#myButton').click(function() {
// ...
});
the value of this is the target DOM element of the event, or the target object if the event is triggered on something other than the actual DOM.
In other jQuery callback situations, like .each() for example, the value of this is generally determined by the nature of the jQuery object.
The upshot of all that is that inside an integrated-with-jQuery function (first sample), you generally don't wrap this in another call to jQuery; it's already a jQuery object. In all other cases, if you want to use this with jQuery facilities, you do have to wrap it.
In that context, "this" is the actual JQuery widget object, which allows you to query things such as widgetName, widgetEventPrefix etc, whereas "$(this)" is just the JQuery object for "this", which you can't get the widget properties using.
this represents the contructor you are in. For example in a click event, the dom element being clicked, in a function the constructor of function;
function test{
this.b = "hello jupiter";
}
var a = new test();
alert(a.b);
Acctually there is nothing special with $(this). There is a function called $ and "this" is it's parameter, like alert;
$(this);
//just like
alert("some string);

When binding event to multiple elements at once, new instance for each element?

I have 100 BUTTONS in my page ( each of them has class='btn').
Also I have a single button which prepare all the other 100 buttons.
<input type='button' onclick='bindTheClickevent()' />
When pressed, - it calls bindTheClickevent() - (which binds the click event to all 100 others).
In the Script Section I put:
function bindTheClickevent ()
{
$(".btn").bind('click',function () {
$(this).css('color','red');
});
}
Questions
1) In memory, how many instances of the anonymous function are created?
2) In memory, Does the bindTheClickevent() function will ever be free (GC)? - please notice that the Bind is called inside the bindTheClickevent function...
3) When, eventually - the bindTheClickevent function will be free to GC ?
Lets make a change
function bindTheClickevent ()
{
$(".btn").bind('click',function () {
changeColor($(this));
});
}
function changeColor(obj)
{
$(obj).css('color','red');
}
Now - after the change
1) Is there any difference if I Do that?
2) In memory, how many instances of the anonymous function are created?
3) Does the bindTheClickevent() function will ever be free (GC) ? - please notice that the Bind is called inside the bindTheClickevent function...
"1) In memory , how many instances of the anonymous function are created ?"
Which anonymous function?
For the inline onclick, you get a function assigned to the onclick property of the element like this:
function(event) {
bindTheClickevent();
}
... or similar depending on the implementation. That function will be free for GC when the element is dereferenced or the function is dereferenced from the onclick property.
With respect to the jQuery code:
$(".btn").bind('click',function () {
$(this).css('color','red');
});
...while the anonymous function is shared, what you don't see is that if the elements in question do not already have a jQuery handler bound, jQuery will internally create a unique function for each element.
That internal handler is what actually gets bound to the element, and when the element receives an event, that handler is invoked, analyzes the event, and invokes the handler you originally passed (if necessary).
This means 100 jQuery bound elements equals 101 unique function instances.
In order to make sure that any handlers bound using jQuery are GC'd, you need to make sure that you always use jQuery to remove DOM elements. If you don't, all the data (including handlers) stored in jQuery.cache doesn't get cleaned up, and so they'll always be referenced via the global jQuery namespace.
EDIT:
Given that there are 100 elements that have the class btn, that don't have any handlers bound by jQuery, then this code:
$(".btn").bind('click',function () {
$(this).css('color','red');
});
...will create 101 unique Function instances.
Why 101?
Well, what jQuery does is the first time you bind a handler to an element, it internally creates a unique generic handler for every element. This is the handler that is actually invoked when an event occurs, and handles all event types.
Your handler function is never actually bound to the element.
So that generic internal handler when invoked will analyze the event that took place, and see if any handlers have been associated with the given element using .bind() that match that event type. If so, it calls the handler that passed.
Now let's say you bind another handler:
$(".btn").bind('mouseenter',function () {
$(this).css('color','blue');
});
...since we're binding to the same elements, they already have the necessary internal handler and another does not need to be created. So all that happens is that the function you pass is referenced internally, and is invoked by the generic internal handler when needed.
As such, given the code snippets above, there now exists 102 unique Function instances.
It looks like only one instance of the function is created in both circumstances. It appears as though References to the anonymous function are attached as the event handlers for each element.
Example - Using a closure to show the sharing of scope between button event handlers.
Note that this can cause interesting behavior if you involve closures because all elements will share the same function (and closure scope).
And no, your declared functions will not be GC'd because of their global scope.
Additionally
To attach them independently (not by reference), loop over the selected elements with .each() and attach the function individually.
Example
$('.btn').each(function() {
$(this).bind('click',function() {
// each '.btn' has it's own copy of
// this anonymous function
}
});
If you do something like these:
for (someiterations)
{
$(myobj).bind("click",function()
{
// ...bla...
});
}
In this case you are creating a new function each iteration.
In your function this is not happening because you are passing the function to a parameter, so there is a place which has stored it's reference (yea the function param) that will do something like this:
for (iterations)
{
myob.addEventHandler(event, funcref);
}
So should be ok, now:
Don't think so, not sure of the syntax however.
1 as I explained
No because it's in the global scope and it's not assigned to an instance, you can think of it as a constant, not as a variable
Note: The anonymous function will not be released, it's referenced by the event handler.

First parameter to event handler

If I have some event handlers registered inline with my markup (deprecated, I know) like
span id="..." onclick="foo(p1,p2,p3)"
how can I access the "event" object in the event handler function foo? Changing the above to
span id="..." onclick="foo(event,p1,p2,p3)"
and then using it in foo like:
function foo(e,p1,p2,p3)
{
if (!e) e = window.event;
}
seems to work but I don't see it documented anywhere so I am wary of using it. In other words, is the first parameter to a inline event handler always the event object if it is named as such in the onclick=... markup? Is this cross-browser so it can be safely used? And if it is not named as such (as in my first example), the parameters are treated like regular parameters and the event object is not passed?
Thoughts?
You're misunderstanding your code.
The string that you put in the inline handler is a normal piece of Javascript code. It does not need to be a single function call; it can even contain multiple statements (separated by semicolons, as usual)
The code in the inline handler will be given a variable called event which refers to the event object.
When you write onclick="foo(event,p1,p2,p3)", you're making a regular function call, and passing the values of four variables named event, p1, p2, and p3 as parameters to the function.
Take a look here. This seems to line up with your example. However, there is some mention of this not working the same way in IE, so you have to check whether the first argument (event object) is defined and if not use window.event.
Another reference here. I frequently find MDC to be helpful.
Ok, so I ran a few tests in Firefox (3.5.8/linux) and here's what I've come up with. I was unaware of 'event' being used like in example 2, but it seems to work correctly in Firefox. However, it is NOT the case that the first variable passed to a function is always the event. 'event' seems to be registered in some global object, but I can't seem to determine which one. (It's not document, or window :P)
The line of code that you have in the foo function
if (!e) e = window.event;
is basically how you have to catch events in Internet Explorer anyway, so it will work in IE and Firefox for sure. And yes, therefore, if you are not passing a variable called 'event', as in your second example, the parameters will be treated as normal parameters, and the event object will not be passed.

Categories

Resources