Could someone please explain the concept of how the 'onlick' property of an element is working, pertaining to its syntax when it is assigned to a function, and its implementation where it is inherited from.
const FOO = document.querySelector('.bar');
FOO.onclick = function (e) {
e.preventDefault();
console.log('button clicked');
}
One area of confusion is regarding how does the browser know (in the implementation) to treat the onclick property as a click event.
How does assigning the onclick property of an element to a function result in the element being able to listen for click events - as opposed to say assigning a function to any other made up property of that element (whats happening behind the scenes?).
Is every element designed to always listen for click events and by assigning specifically the onclick property to a function it executes that function with the click event?
I am a beginner to programming, JavaScript, and object oriented concepts. I was hoping if someone could offer their insight and explain these concepts in any way.
Many thanks for your time and help
Yes, every element is designed to always listen for click events. By writing Element.onclick = function(){}, you are assigning a function to the Object (Element)'s onclick property, which will be executed when the element is clicked on. Essentially an Element is an Object, and its properties can be set just like any other Object.
An Object consists of property and value pairs. Consider the following object.
var obj = {color: "red"};
This Object has only one property, color, which has the value "red". If you were to assign another property to the Object, you could do this:
obj.background = "green";//or obj["background"] = "green";
You can assign a function as the value of one of an object's properties. An Element's onclick property is always run when it is clicked. When you update the value of it, it replaces the old value, and the new function you provide will be run when the Element is clicked on.
Related
Say I have some HTML anchor elements and I would like to set a handler for each of them.
window.onload = function() {
// I select all of the anchors
var myAnchors = document.querySelectorAll("a");
// I iterate through the anchors and set a handler to each of them
for (var i = 0; i < myAnchors.length; i++) {
myAnchors[i].onclick = handleControl;
}
}
function handleControl(e) {
var id = e.target.getAttribute("id");
}
I'm unable to understand how setting an handler passes an argument to the handleControl function. In other words, how does myAnchors[i].onclick = handleControl; pass a value e to the handler?
I got this code from a JavaScript programming book.
It doesn't. Basically you're saying what function should be used when, at some point in time, a value becomes available. So for example, there you're saying that when the link is clicked, the function you specified, handleControl, should be called. The parameter e is passed to it by the browser, which represents information about the click event.
So think of it like:
browser detects a click on the link
it creates an "event object" that contains information about the event
it invokes the handler function you specified with the "event object" as an argument. You can imagine it does something like anchor.onclick(event_info), where event_info corresponds to the e parameter you have on handleControl.
Keep in mind, this isn't necessarily exactly what's happening, but the point to answer your question is that the parameter comes from elsewhere (in this case, the browser), and is passed to an invocation of the function you specify.
I believe you just want to know the how?
In layman terms:
hi, im an element, and you can click on me
Ok, when I click on you I want the function handleControl to be executed, here you got a reference to that function.
Thank you
User clicks
Oh boy! I'm clicked, let see if i got a function reference on my onclick attribute
Yes.. yes i do! Okay, let me fire this function and give some event information while doing this
Calling this.onclick(e); with e being an Event object, and this.onclick the reference to the handler function
If this is not what you asked for, i feel stupid and you can ignore this ;)
Your question is how setting an handler passes an argument to the handleControl function. In other words, how does myAnchors[i].onclick = handleControl; pass a value e to the handler?
So, onClick event will trigger one function:
function(e) {
// Whatever you want to do with clicked 'anchor' e, do it here
}
Internally you will get the clicked anchor object as e here.
In your example the function
function handleControl(e) {
var id = e.target.getAttribute("id");
}
does the same.
So when you do
myAnchors[i].onclick = handleControl;
You are actually registering a callback function to
myAnchors[i] element's click event.
The dom engine of the browser keeps track of all the callbacks for each event for each dom element.
Now when an event occurs for element, then dom engine calls all the callbacks corresponding to the element and that event. But it calls with a parameter which is event object.
Here the function handleControl is subscribed to the DOM Element.
So if any click event triggers from DOM, the Event Info e will get passed to the subscribed method.
I started learn JavaScript and in some tutorials I saw that was used document.getElementById() and in some tutorials in same case was used "this" that was passed from control that fired the event.
Maybe someone can explain why should I prefer one way over an other?
Thank's a lot
Are you referring to the documentation from this MDN page on addEventListener? Make sure you look at the section on the value of this within the handler. The earlier examples use document.getElementById to look up an arbitrary element. For example, you could attach a click handler to a button (or any other element), that modifies some other element when it is clicked. In this case, you don't really do anything with the object that generated the click event. So in the handler, you will use document.getElementById to modify the element you want.
However, in some other cases you would like to modify the element itself. For example, you might want to change the color of an element the user clicks on, to let them know that its state has changed (a toggle basically). In this case, you want access to the element that actually generated the event, and you can do that by using this. Here this means "I am the HTML element who generated this event".
In a more general context, this can have different meanings, and it depends on the use case. In the context of event handlers, this means the element that generated the event. In other cases, such as in object-oriented JavaScript, this in a function can refer to the parent object of that function. JavaScript is flexible enough that a function can have whatever this value the author decides. In well-written code, this has a meaning that generally makes sense, and usually refers to some sort of "owning" context or object.
Functions assigned as Events have their this context as the Object to which the Event belongs. In other words, the Element itself.
// you can get Element a number of ways including document.getElementById('idHere')
function someFunc(){
this.style.color = 'blue';
}
Element.onclick = someFunc;
or
Element.addEventListener('click', someFunc);
or
Element.onclick = function(){
this.style.color = 'blue';
}
or
function someFunc(context){
context.style.color = 'blue';
}
Element.onclick = function(){
someFunc(this);
// more code here
// could use document.getElementById('whatever') to get HTML Element with id='whatever'
}
I am working with Esri's Javascript Library 3.10, which is based on Dojo. I'm having an issue with scope, and despite trying different variations, I'm still having the same result. There is probably a much better way to do this, but I can't seem to figure it out.
I want to iterate through an object containing keys to a set of checkboxes, then assign an event handler using dojo/on to set a value based on the key, however, the key, "setting" inside the On(...) function is the same for all four iterations.
for (var setting in this.appSettings) {
console.log(setting); //prints four different things
if (this.hasOwnProperty(setting)) {
this.checkboxHandles[setting] =
On(this[setting], 'change', function (checked) {
//this.setValue(setting, checked)
console.log(setting) //prints the last 'setting' whenever checkbox is changed
});
}
}
So setting inside the On function is always the same value, even though the for loop is looping through four unique keys. First, why does this happen, and what are some suggestions to solving this?
That's one of the JavaScriptessentials. Closures always refer to their outer scope variables by reference. This means that the event handler references to the setting variable. However, when the for-loop continues, the setting variable is referencing the next value in the array.
The solution to this problem is what we call an IIFE or an Immediately Invoked Function Expression.
If you replace the event handler with a function that returns a function, for example:
On(appSettings[setting], 'change', function (setting) {
return function(checked) {
//this.setValue(setting, checked)
console.log(setting) //prints the last 'setting' whenever checkbox is changed
}
}(setting));
So, what happens here is that the setting variable is passed to the function wrapper, the advantage of this is that they are passed by value, not by reference.
So, if you access the setting variable from inside that function, it will refer to the locally scoped setting variable which is passed to that function.
How weird it may look, it works: http://jsfiddle.net/8sxqn53d/
An interesting slidedeck that explains this behavior can be found here.
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.
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.