Passing a value to a function in JavaScript - javascript

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.

Related

Can't understand how function's parameter work in a particular piece of code [duplicate]

This question already has answers here:
What exactly is the parameter e (event) and why pass it to JavaScript functions?
(5 answers)
Closed 4 years ago.
There is an example in "Head first JavaScript" book. This piece of code is unblurring an image on click. The code works, but I don't understand how, though it's an extremely simple piece of code.
The function init is called when a window is loaded. getElementsByTagName gives an HTMLCollection. A click on an image invokes a showAnswer function.
Now there is a mystery for me.
window.onload = init;
function init() {
var images = document.getElementsByTagName("img");
for (var i = 0; i < images.length; i++) {
images[i].onclick = showAnswer;
}
};
function showAnswer(e) {
var image = e.target;
var name = image.id;
name = name + ".jpg";
image.src = name;
}
There should be a parameter e. How this parameter is being created, from where? When showAnswer is called in init, there are no parameters given to it.
Considering the fact that I'm using a .target method on it, it should be an object. How does the browser know that this object has name e?
Why images[i].onclick = showAnswer; and not showAnswer();?
images[i].onclick = showAnswer is defining the event handler function to be run when images[i] is clicked. If you were to use showAnswer() there, it would run immediately in the init function which is probably not what you want.
images[i].addEventListener('click', showAnswer) is another way to write that, which might be more intuitive.
Event handler functions pass in an event object, which is what the 'e' is referring to. 'e.target' is referring to the element itself.
A reference for DOM events:
https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Events
(breaking myself of the habit of answering questions in comments...)
There should be a parameter e. How this parameter is being created, from where?
When you do images[i].onclick = showAnswer, that assigns "showAnswer" as the event handler for the "click" event for the DOM element named in images[i].
Later, when the user clicks that element (or the event gets triggered by some other method), the browser constructs the Event object, which contains a whole lot of information about that specific event. It then calls your event handler, passing along the Event object as the first (and only) parameter.
When showAnswer is called in init, there are no parameters given to it.
In your init function, you don't call showAnswer; you assign it as the event handler for clicks on images. The event is what passes the parameter to the handler, not your init.
Considering the fact that I'm using a .target method on it, it should be an object. How does the browser know that this object has name e?
That's the name you gave the parameter in the function. You could use any name, but e or evt are a common convention for event objects.
Why images[i].onclick = showAnswer; and not showAnswer();?
If it were showAnswer() you'd be assigning the return value of the function to the click handler. (Which in this case would be undefined, because showAnswer doesn't return anything.) With showAnswer you assign the function itself to the handler.

Passing both event and parameter to onchange bound function in JavaScript

In JavaScript I'm attempting to set an onchange callback that accepts a parameter. However, how I'm currently doing it overrides the event object that is created. I don't actually need the event for my purposes, but I would like to know how I can capture both the event and any passed in parameters in case my needs change.
EDIT: For clarity, this onchange event could be called both programatically and by a user. There may be an instance where I'm creating an empty select element so the user can pick what they want, or creating a populated one based on some other interaction.
EDIT: Also, the below is a simplified example of a much larger code base. Assume that the scoping is not global of any of the variables. I'm really looking for an answer of how to specifically be able to capture both and event object (when called via user interaction) and another object (when called via code). It feels like having the atr parameter mean different things in different contexts is hacky - but I come more from a strongly typed background so it might be just me.
function update(atr) {
...
}
document.getElementById("myelement").onchange = update;
var atr = {"id":1,"param":"val1"};
// This gives me atr in the function as defined above
document.getElementById("myelement").onchange(atr);
// This way, however, gives me atr in the function as the event
document.getElementById("myelement").onchange();
What I would really like is something like this:
function update(e, atr) {
// Now I have e as the event and atr as the value I've passed in
}
document.getElementById("myelement").onchange = update;
var atr = {"id":1,"param":"val1"};
// This gives me atr in the function as defined above
document.getElementById("myelement").onchange(atr);
However the above code doesn't work. I suspect that I have to do something with bind() but as far as I can understand that I would simply be overriding the event's (this) object in the function like I'm doing now implicitly.
The accepted answer in this question Similar Question is basically what I want to do, but that is with React JS and I would like to do this without any frameworks. I've been trying to search for multiple parameters and onchange events and primarily getting React or unrelated responses. Either this is a harder question than I think, or I'm searching for the answer in completely the wrong way.
I will explain what happens in the linked answer as you mentioned that you want to achieve the same behaviour.
So:
<fieldset onChange={(e) => this.props.handleChange("tags", e)}>
This React code attaches anonymous function with one parameter e to the fieldset as onChange listener. This function in its body invokes another function, passing e with additional parameters.
Translating this into your code, you would like to achieve something like this:
function update(e, attr) {
// e is instance of Event
// attr is additional parameter
}
document.getElementById("myelement").onchange((e) => update(e, attr));
// or without ES6 arrow function:
document.getElementById("myelement").onchange(function(e){ update(e, attr); });
Also, be advised that proper way of attaching event listeners is by addEventListner API.
I'm not sure I understand exactly what you're trying to do, but first you need to distinguish between the case that the event is triggered by the user and the case that you call the event programatically, when you call it programatically there is no event.
you can do something like this:
You mentioned that you use select, the logic is that when a change in the select occurs the event is thrown and you get the selected value, in your case the value can be the content of the atr var:
HTML
<select id="myelement" onchange="update(event)">
<option value='{"id":1,"param":"val1"}'>val1
<option value='{"id":2,"param":"val2"}'>val2
</select>
JavaScript
function update(e) {
var atr = JSON.parse(document.getElementById("myelement").value);
//now you have access both to the event and the 'parameter'
}
This covers the case when the event is triggered by the user, when you want to trigger the event programatically, since there is no event, use a different function that take the atr parameter.

How does an event object work in this code

window.onload = unblurMonaLisa;
function unblurMonaLisa() {
var images = document.getElementsByTagName("img");
for (var i = 0; i < images.length; i++) {
images[i].onclick = showAnswer;
}
};
function showAnswer(eventObj) {
var image = eventObj.target;
var name = image.id;
name = name + ".jpg";
image.src = name;
};
The above code switches a blurred image for an image that is not blurred when said image is clicked. The code works just fine. My questions are about the eventObj that is passed to the showAnswer function. When is the eventObj created in this code? Is the scope of eventObj local? If I assigned the onclick event to two different variables would that create two eventObj and if so how would I access them individually?
When is the "eventObj" created in this code?
When the the event you are interested in happens, in this case a click, your callback function is automatically executed by the environment. When the callback function is executed, it is given an event object as an argument, which you can choose to ignore it in your function signature. (i.e. your showAnswer function could be like this function showAnswer() {...}). The event object is a regular object that holds information about the event that just happened.
Is the scope of "eventObj" local?
Yes, it is.
If I assigned the onclick event to two different variables
You cannot add two different callback functions by using .onclick. You should use addEventListener() if you want to add multiple callback functions to the same event.
would that create two "eventObj" and if so how would I access them individually?
The event object that would be created for that event would be just one, and it would be passed to all callback functions.
For more info on events read this: https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events
Also check out the answer to this question, which is similar
UPDATE:
To answer to your comment:
the browser creates a unique Event Object every time an event occurs whether we have a handler listening for that event or not?
Correct.
We can pass this unique object to our handler
Correct again.
I noticed on your other post they used "e" in place of "eventObj"
You can name it whatever you want in your function. As long as you put something as your function parameter (function(e) {} for example) your function will accept the event object.
People usually name that parameter e, short for event, or event, to indicate what kind of thing is this parameter, but you can name it however you want, like any other parameter of any other function.

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" in jquery widget callback

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.

Categories

Resources