Understanding arrow functions - why does this not work and this does? - javascript

I am trying to convert all of my older code to use arrow functions...
The following works:
$p_worklist.mouseover (()=> {
closemenues();
console.log ("Show Worklist");
$div_worklistmenu.show();
});
However this does not
$p_worklist.mouseover =()=> {
closemenues();
console.log ("Show Worklist");
$div_worklistmenu.show();
};
The difference is that the first function wraps the body in parens while the second does not but rather replaces the first paren with equals sign and eliminates the second one.
Trying to learn...
Thanks

The first one is calling $p_worklist.mouseover, and passing in a function. Jquery will then do whatever internal code it needs to do to set up the event listener, and when a mouseover happens, it will call the function you gave it.
The second one is assigning to $p_worklist.mouseover, thus overwriting what used to be there with the function you created. No other code is run, and no event listeners are set up.
Jquery's api expects you to call it, so option 1 is the right way to go.

In Your first example You invoke mouseover method, hovewer in next exaple You just overwrite that method and You don't invoke it

The problem is not the arrow function, but your usage of the mouseover attribute to set an event listener.
In JQuery (which, given your use of $, I'm assuming you're using), mouseover is a function that takes another function as an argument. So, you would pass an anonymous arrow function exactly as you do.
In vanilla JavaScript, however, the mouseover attribute is a pointer to the function to be called as an event listener.
If you're using JQ:
$('selector').mouseover(() => {
// ...
});
If you're using JS:
element.mouseover = event => {
// ...
}
Of course, you can override the JQuery method yourself by using the setter, but that's probably not what you're looking for.

mouseover() is a jQuery method. Like all jQuery event-handling methods, it takes the handler function as a parameter, so you have to call the method.
When you assign to $p_worklist.mouseover, you're replacing the method with a new function, not calling the method. That's not how you bind event handlers in jQuery.
You're confusing the jQuery method calls with DOM onXXX properties, where you write something like
element.onmouseover = ()=> {
closemenues();
console.log ("Show Worklist");
$div_worklistmenu.show();
};

Related

Is it a bad practice to use the bind() method for many elements?

Assuming there are many popups (like more than 30) in my page, and I need to make them listen to a click event handler. Below is Example 1 in the jQuery syntax:
$('.popup').on('click', function () {
// do something
});
I have learned a suggestion in the past that the Example 1 approach is bad for performance/memory (I can't recall the exact reason) because each element listens to an independent anonymous function. And the suggestion said that it is better to name the event handler and then make all the elements listen to the named event handler, like the following Example 2:
function clickEventHandler() {
// do something
}
$('.popup').on('click', clickEventHandler);
Now, for each popup, I need to pass a variable which is specific to the popup, into the named event handler. The approach I think of is using the .bind() mehtod like the following Example 3:
function clickEventHandler(someInfo) {
// do something
}
$('.popup').each(function () {
$(this).on('click', clickEventHandler.bind(null, $(this).attr('data-some-info')));
});
However, it concerns me that the description of the .bind() method says:
The bind() method creates a new function...
My question is: What does "creates a new function" mean exactly? Does it mean creating an independent anonymous function every time the method is used and is as bad as Example 1 when it comes to performance/memory?
Approach 1 is just fine - you're only creating a single function, and passing it to every event handler.
Now, if you had something like
$('.popup').each(function() {
$(this).on('click', function () {
// do something
});
});
that would indeed be very slightly wasteful, because you'd be creating a new callback function for every iteration in the loop.
But
Assuming there are many popups (like more than 30) in my page
30 is absolutely nothing with modern hardware. Now, if you had a thousand, or ten thousand, then there might be something to consider, maybe.
My question is: What does "creates a new function" mean exactly? Does it mean creating an independent anonymous function every time the method is used and is as bad as Example 1 when it comes to performance/memory?
Example 1 is just fine - but calling .bind in a loop is somewhat similar to my snippet above, in which there are many separate functions in memory.
To achieve what you want:
Use event delegation (optional - this way you only attach a single listener to a parent, instead of a
listener to every single element)
Check the attribute inside the handler instead of outside it
function handleClick(e) {
const someInfo = this.dataset.someInfo;
// rest of function
}
$(document.body).on('click', '.popup', handleClick);

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 to call a click( ) attribute?

function dosomething(){
alert($(this).attr('id')+' called with '+$(this).innerHTML);
}
$('#myElement').click(dosomething);
here is the HTML:
<div id="myElement">click me</div>
Clicking the element works. But the following call from another location does not:
dosomething($('#myElement'));
Working with objects in Javascript is still frustrating to me. Can anyone explain WHY #2 doesn't work, and an elegant way to handle both cases? here is a jsfiddle for reference:
http://jsfiddle.net/sqb6bkwr/
Your function dosomething() does not accept any arguments. Therefor it will not work. You can choose to use $('#myElemenet').trigger('click');, or choose to have your function accept an argument which sets the element:
function dosomething(el) {
el = (el) ? el : $(this);
alert(el.attr('id')+' called with '+el.innerHTML);
}
// My suggestion won't work.
// Use dosomething.call(el) instead.
dosomething($('#myElement'));
This doesn't work because the function dosomething() is not expecting the element to be taken in as the first parameter, and passing it as a parameter does not automatically set it as this.
The way you should call that method depends on your intended behavior. If what you want is to just call that dosomething function on that element, then you can do this:
dosomething.call($('#myElement')[0]);
But if your objective is to simulate a click on that element, which would trigger any other click event listeners that may be on that element, then you should do this:
$('#myElement').click();
// or, as #Karl-AndréGagnon pointed out, $('#myElement').trigger('click');
The difference may seem small, but knowing what you really want to happen will probably save you from some headaches down the road.
This is because the this value of your function was not set. You pass an argument to your function, but expect the this value to be the argument.
You should instead call that like this:
dosomething.call($("#myelement")[0]);
This will call the function with the this value of the #myelement. The [0] is there because you want the native DOM element, not a jQuery array-like object. This is why you wrap the this in $(this) in your function.
Try this:
var domething;
$(document).ready(function(){
dosomething = function(elem){
alert(elem.attr('id')+' called with '+elem.attr('title'));
}
$('#myElement').click(function(){
dosomething($(this));
});
});
Working fiddle: http://jsfiddle.net/robertrozas/3z0pg9b1/1/

jQuery: determine if a function was called as a jQuery event handler (callback)

I have a JavaScript function that can be called either directly, either as a callback from a jQuery event.
In the latter case, the jQuery Event object will be passed as an argument. When I call the function directly, I also provide an argument (of a different kind, but still an Object).
Therefore I need to be able to check (within the function body) how the function was invoked so as to know what sort of argument it's dealing with.
Is there a simple way to do that?
Accept the argument and then see if it's instanceof jQuery.Event:
function yourFunction(e) {
if (e instanceof jQuery.Event) {
// ...it was an event handler call
}
}
Live Example | Source
I would recommend avoiding this sort of thing, though. Instead, I'd recommend having the event handler call the target function (rather than using the target function as the actual event handler), if you need to distinguish the behavior depending on how it was called.
From your comment on the question:
What I tried simply is: when the function is called directly, I use a null value in first position and the useful argument in second position.
In that case, it's even easier:
function yourFunction(e) {
if (e) {
// ...it was an event handler call -- or at least,
// a non-falsey argument of some kind was passed in
}
}
You don't even have to pass null into the function, just call it with no arguments. When you do that, e will be undefined, which is falsey. (The event object jQuery passes the handler will never be falsey.)
try typeof() operator inside the if condition. if argument type comes out to be same, you may try assigning a unique property both the passing objects/events in either case and then perform a check agains that property...

Event in a function with jQuery

I know how to use events with jQuery in the classical way for example:
$("#moveArea").mousemove(function(event){
$("#info").empty().append("pageX is: "+event.pageX);
});
Demo: http://jsfiddle.net/Ey7kP/
My question is how to pass the event in a function I have already create. I need something like the following, but I don't know how to achieve this
function cursorPos(event) {
$("#info").empty().append("pageX is: "+event.pageX);
}
$("#moveArea").mousemove(cursorPos(event));
Just do
$("#moveArea").mousemove(cursorPos);
Since you're referring to the function and not calling it, there's no need for passing the arguments. jQuery will call it for you and pass event to it.
You dont have to pass any event variable. jQuery will pass it when it executes the handlers. Just say.
$("#moveArea").mousemove(cursorPos);
There's no need to pass the argument as it is defaulted to event. By placing the function name itself, cursorPos within your mousemove() event, you are capturing the necessary event thus rendering the need to pass the argument unnecessary.
function cursorPos(event){
$("#info").empty().append("pageX is: "+event.pageX);
}
$("#moveArea").mousemove(cursorPos);
Working example: http://jsfiddle.net/8v4uE/

Categories

Resources