The following statement on MDN seems not true:
When the event handler is invoked, the this keyword inside the handler
is set to the DOM element on which the handler is registered.
(source)
This statement is about an on<...> event handler not addEventListener.
<p><a id="link" href="#" onclick="EventHandler();">click me</a></p>
<script>
function EventHandler() {
console.log(this);
//the this keyword inside the handler is NOT set to the DOM element
//it sets to the window object
}
</script>
which contradicts with the statement on MDN.
Of course, you can pass the this as an parameter to EventHandler or simply use addEventListener.But that is off topic.
The topic of the question isthis and an on<...> event handler.
My question is: Is that my understandig is not correct or is the statement on MDN is not correct?
This statement is true whenever the event handler is inlined, as it's stated in the link right after the sentence you quoted.
But, in your case, MDN specify :
When a function is used as an event handler, its this is set to the
element the event fired from (some browsers do not follow this
convention for listeners added dynamically with methods other than
addEventListener).
I've tested in latest Chrome & FF, these browsers don't follow this convention.
According to the specs, this inside event listeners/handlers will always be the current target of the event:
Event listeners:
Call listener's callback's handleEvent, with the event passed
to this algorithm as the first argument and event's currentTarget
attribute value as callback this value.
Event handlers
If E is an ErrorEvent object and the event handler IDL attribute's type is OnErrorEventHandler
Invoke callback with five arguments, [...] and with the callback this value set to E's currentTarget.
Otherwise
Invoke callback with one argument, the value of which is the Event object E, with the callback this value set to
E's currentTarget.
Related
Let's look at the following code:
const div = document.getElementById('foo');
div.addEventListener('click', function() {
console.log(this);
});
<div id="foo">click me</div>
This button will always log the dom element I'm clicking. I've always known this to be true, and I know that I can use an arrow function here to make the value of this the window. We are assuming non arrow function syntax for this question.
To my knowledge, the value of this gets its value based on how the enclosing function is invoked. The callback of this event listener certainly doesn't get called on the dom element.
In my mind, when the div gets clicked, it adds the anonymous function to the message queue. When the queue is empty it invokes the anonymous function in a global execution context (perhaps this is where I'm wrong).
If it is in the global execution context where this anonymous function gets invoked, shouldn't the value of this be the window?
Which leads back to the title question, why is the value of this in a dom event listener callback not the window? (Assuming the callback is not an arrow function)
It's because it's a method attached to the div object - think of it like this:
const div = {
addEventListener: function(event, callback) {...}
};
In this example, this would refer to div as you would expect.
This is what's actually happening in your code, only it's being defined differently.
The value of this within the handler
It is often desirable to reference the element on which the event handler was fired, such as when using a generic handler for a set of similar elements.
If attaching a handler function to an element using addEventListener(), the value of this inside the handler is a reference to the element. It is the same as the value of the currentTarget property of the event argument that is passed to the handler.
When I started learning javascript I thought that the browser would simply run the javascript in onclick attribute of elements as if it was inside a script tag. However I realize that is not the case because you can use certain parameters inside these attributes. For example onclick="foo(this, event)" passes the event triggered and the element that the event was triggered on to function foo.
I wonder what other parameters can be used inside these tags? The documentation on w3schools does not mention anything about this. I would also like to know how these attributes are implemented; how is the function called and in what scope (I know that foo will be called like element.foo() but this is not true for other javascript expressions). Is there a comprehensive documentation anywhere? my best guess at the moment is that the implementation looks like this:
element.onclickfunction = function(){
var event = new Event(...)
eval(element.onclick)
}
Edit:
none of the responses really answered my question. I was interested to know what other special parameters can be passed to the function specified in an event handler and how the parameter this is interpreted to be the Element. I am picking the best answer because he provided a link to mdn where EventHandler interface is described.
In JavaScript, you can register an event listener on any DOM element, the document itself, the context window, or any other objects that support events. There are a whole bunch of events that are defined in a whole bunch of web standards. A good resource that lists these out is the Mozilla Developer Network.
The EventListener that gets registered to the event only takes one parameter: the event. You can see that it only has one method, handleEvent, which takes only one parameter.
To pass other stuff into the event handler, you can take advantage of closures or bind(). I'm going to borrow from this answer to demonstrate how bind() works:
elem.addEventListener(function(a1, a2, e) {
// inside the event handler, you have access to both your arguments
// and the event object that the event handler passes
}.bind(elem, arg1, arg2));
Binding elem, arg1, and arg2 to the function means that the this of the function becomes elem, and the first two arguments of the function become arg1 and arg2. The e argument is still the event that triggered the function, just like always. Binding stuff like this is super useful when you want to pass stuff from outside the event handler to inside the handler. You can read more about bind() here.
With regards to your specific question about onclick(), the principle is basically the same except your registering a specific click event handler to an element. It too only takes one argument, and that is the MouseEvent object. But like with any function, you can still bind other arguments to it. See here for more info.
Sometimes I see functions with parameter like this:
$('#my_div').bind("mouseover mouseenter", function (e) {
console.log(e);
var el = $("#" + e.type);
var n = el.text();
el.text(++n);
});
I don't get what is being passed into the function. I would understand it if it is something like (function(e){ .... }(parameter); but it is not. Can someone shade some light
jQuery will actually call that function, since functions are first class citizen objects in JavaScript, so it can be passed as an argument. e is for event args which jQuery will provide.
This pattern is called "callback".
There's always a parameter being passed, it's just that you're not the one passing it directly. The browser is.
In this case, you're talking about the event handlers. Basically what happens is that when the event is triggered, the browser will pass an event object to the callback of the event listener (the callback you provided).
If you want to know what's inside the event object, look into this mozilla docs page https://developer.mozilla.org/en-US/docs/Web/API/Event
If you want to know how browsers handle the whole passing of event object, here's the specification: http://www.w3.org/html/wg/drafts/html/master/webappapis.html#the-event-handler-processing-algorithm look under 4) where it says "process the event E as follows"
The function isn't actually being called yet. This is telling the browser to call that function for you when the 'mouseover' or 'mouseenter' event happens. In this case, when the browser does that it will pass an event object which will get bound to the local variable e in side of the function.
I was trying to find the meaning of this keyword inside event handler function in the DOM level 3 event spec.
As per my experiment this refers to the event.currentTarget object.
Is this behavior mentioned somewhere in the standard?
As per "JavaScript The Definitive Guide" book this refers to the event target which seems to wrong. event.currentTarget seems more logical as event handlers are invoked as the method of the HTML element object.
Can someone please clarify?
In case of bubbling I see "this" changes and means the
event.currentTarget.
Indeed, the Definitive Guide is wrong in that case.
I found a reference in the HTML5 event handler processing algorithm:
Invoke callback with one argument, the value of which is the Event object E, with the callback this value set to E's currentTarget.
The DOM level 3 event specification didn't say much about it in previous versions - it was meant to be language agnostic. The Appendix F: ECMAScript Language Binding just stated
EventListener function:
This function has no return value. The parameter shall be an object that implements the Event interface.
However, current versions omitted these bindings. And in its Glossary appendix event listeners are described:
event handler, event listener: An object that implements the EventListener interface and provides an EventListener.handleEvent() callback method. Event handlers are language-specific. Event handlers are invoked in the context of a particular object (the current event target) and are provided with the event object itself.
Also, the upcoming DOM Level 4 draft, whose goals contain aligning the DOM with the needs of EcmaScript, does explicitly state in the Dispatching Events section:
If listener's callback is a Function object, its callback this value is the event's currentTarget attribute value.
In an event handler for an element, with default capturing (false), this will refer to the element which detected the event. Either one may be used.
For example:
element.addEventListener('keydown', function (event) {
// `this` will point to `element`
}, false);
When capturing an event (true), say at the window level, event.target, will refer to the element which originated the event, while this will refer to the capturing element. For example:
window.addEventListener("error", function (event) {
event.target.src = 'some_path';
// `this` will point to window
// `event.target` will point to the element that had an error
}, true);
I hope this exemplifies the difference between each.
Often I see javascript code where event handlers (like onmousemove) are assigned dynamically.
Example:
document.getElementById('foo').onmousemove = function(e)
{ /* do some stuff with event e */ }
Apparently this 'e' parameter is some kind of event object. Where does that come from, as in: who or what defines what this 'e' parameter is when the function is called, and can I also do this in static html?
I mean like this:
<div id='foo' onmousemove='Bla(e)'> ... </div>
What should I fill in for 'e' to get that same event thing? And can I also combine that with more parameters, like
<div id='foo' onmousemove='Bla(this,e,4)'> ... </div>
where e is, again, supposed to be the event object?
Event handlers are defined as callback methods. A callback is (hence the expression) called from another process at a later time. This is done by the environment (the browser in this case) at the time an event fires.
it calls your callback function and passes in the event object.
The event object is stored in window.event inside of any event handler, so you do not need to worry about your handler conforming to accepting it as a parameter.
In your second and third examples, the e parameter will be passed as undefined because no variable e exists in that scope (unless you have a global e).
This is what's known as a callback method. The event is initially created by the operating system, sent to the web browser, which then passes it off to you in javascript Event object.
I'm not sure, but I think the event is an object instance of ActionEvent. I don't think ActionEvent can be manually instantiated, so you can't do it in static HTML. Even if it would be possible, it certainly wouldn't be best practice.