Multiple functions on click prevents function with event parameter - Vue - javascript

If I have multiple functions passed to a click event i.e.
#click="
inputHandler();
sendToken(computedUser.email);
responseMessage();
"
The function with an event parameter:
inputHandler(e) {
// code
}
Won't run. If I pass it on it's own:
#click="inputHandler"
It works fine.
Why is this and how can I get around it?

Internally Vue uses some RegExps to decide what form of event handler you're using.
If it seems to be the name of a method it will call it and pass the event.
If it seems to be inline code it'll just run it. In this case the event object is accessible as $event.
So:
#click="inputHandler($event)"
is roughly equivalent to:
#click="inputHandler"
Strictly speaking these aren't quite the equivalent, for example with component events that emit multiple arguments you'll only get the first one using $event like this.
See https://v2.vuejs.org/v2/guide/events.html#Methods-in-Inline-Handlers
For a deeper understanding see the Vue source code:
https://github.com/vuejs/vue/blob/0baa129d4cad44cf1847b0eaf07e95d4c71ab494/src/compiler/codegen/events.js#L96
Give your eyes a few minutes to adjust and it isn't too difficult to understand.
Personally I try to avoid anything more complicated than a single method call in the inline listener. Instead I'd suggest having something like #click="onSendClick" and let the method onSendClick worry about the details.

If I recall correctly, vue creates a wrapper function, if the passed value isn't a function. So
inputHandler();
sendToken(computedUser.email);
responseMessage();
actually get's turned into
function wrapper(){
inputHandler();
sendToken(computedUser.email);
responseMessage();
}
And as you can see the arguments passed to wrapper are lost.
The easiest way to fix this is probably to create a new method that accepts the event parameter and calls all of your function and use that one in the event handler.

Related

Whats going on under the hood here for event handler? Does it apply to other functionality?

Hi so I am just wanting to understand how javascript/react is handeling whats going on when I have a function triggered from an event . Im going to show three different code segements, 2 of which work, one of which didnt, and see if I am understanding them correctly. :)
setField(e){console.log("in setfield", e)}
<select multiple className="form-control" id="sel2" name="sellist2"
onChange={() => this.setField()}>
this doesnt work because it is passing a null value to a function that expects a value, in this case e. however, if the function was just submitting something already in state, that would be reasonable way to do it.
setField(e){console.log("in setfield", e)}
<select multiple className="form-control" id="sel2" name="sellist2"
onChange={(e) => this.setField(e)}>
this works because it is getting the event as e and then passes it to the function. How does it know to associate the event to e? does this functionality extent to other things when dealing with html?
setField(e){console.log("in setfield", e)}
<select multiple className="form-control" id="sel2" name="sellist2"
onChange={this.setField}>
This one I really dont understand why it works, but it does. I am assuming that it inherently know to pass the event as default to the function. again does this inbuilt logic occur anywhere else, maybe not neccessarily for events?
Thanks again. Im new to javascript so it is interesting to learn all the idosyncracies of javascript. sadly most of then learning is through frustration of debugging!
All functions in javascript can be passed an arbitrarily large number of parameters, even if the function doesn't do anything with them. For example, the following is legal, if useless:
function thingThatExpectsZeroParams() {
console.log('hello');
}
thingThatExpectsZeroParams(1, 2, 3, 4, [5], 'six');
Six pieces of data were passed into the function, but nothing happened with them because the function didn't do anything with them. To get access to the values passed in, you just need to pick a name that you're going to call it. This name is local to your function and can be anything you want.
function callback(event) {
console.log(event);
}
// I could also just have easily called it something else, and the code would work just as well
// function callback(quesoBurrito) {
// console.log(quesoBurrito);
// }
callback('hello');
So when you set up a listener like this:
onChange={() => this.setField()}
Your function will be called, passing in the event object. But since your function doesn't give the first parameter a name, you have no way to access it, and then when you call this.setField, you pass nothing in there either.
In contrast, with this listener:
onChange={(e) => this.setField(e)}
Your function will again be called, passing in the event object. This time, you named that argument 'e', and can then do with it what you will, in this case forwarding it along to setField.
And when you do this:
onChange={this.setField}
You cut out the middleman. this.setField will get called directly, passing in the event object. Presumably, setField is expecting this, and does something with the object it was passed.
It's not complicated. Whatever Javascript function you assign to onChange will get called at the appropriate time and passed one single argument generally known as e. You can give that argument any name you want in your function (or not declare it at all), but the first argument will be there.
So, when you do this:
onChange={() => this.setField()}
You are ignoring the argument passed to your event handler and then inside that event handler you're calling this.setField() which to no surprise does not pass any argument to setField(). In reality, what's actually happening is this:
onChange={(e) => this.setField()}
How does it know to associate the event to e? does this functionality extent to other things when dealing with html?
The DOM specification says that when an onChange event handler is called, it will be passed the e object as the first argument (whether you declare it or not, it's there as the first argument to the function). This is just how callbacks work in Javascript. The caller of the callback decides what arguments they are going to pass the callback. It's up to you when you declare your callback whether you declare the argument and use it or not. Regardless of how you declare it, the caller passes it.
For this section of code:
onChange={this.setField}
You are say that the function you want to be the onChange event handler is your setField() method. So, no surprise, when the DOM calls that method, it will pass the event object as the first argument just like in the above example.
This one I really dont understand why it works, but it does. I am assuming that it inherently know to pass the event as default to the function. again does this inbuilt logic occur anywhere else, maybe not neccessarily for events?
As described earlier the DOM specification is where this event handler is specified. In the definition of that, it describes what arguments will or won't be passed to whatever callback function you register as this event handler. This is true of all callbacks in Javascript. The caller decides what it will pass to the callback. This is not specific to this type of event handler. Callbacks are used lots of places in Javascript. Even something like array.filter() takes a callback and it's the caller of the callback (the implementation of the Array object in that case) that determines what arguments are passed to the callback.

Why is JQuery messing with the params I want to bind to a click handler?

In short, I'm doing this:
function myHandler(a,b,c,d){
doStuffWithMyParams(a,b,c,d);
}
Then somewhere else:
jqueryElem.click(myHandler.bind(a,b,c,d));
When I do, some of the parameters passed (a,b) are read correctly. But the third (c) is a JQuery event object. I've also tried binding the args as an array. Then, the first param becomes the event object.
Totally perplexed here. Thanks in advance for any direction on this.
With the code myHandler.bind(a,b,c,d), the argument a is the context that bind() uses
So my handler is actually seeing this
function myHandler(b,c,d,event){
So I have a feeling you want
jqueryElem.click(myHandler.bind(this, a,b,c,d));
Building on what epascarello wrote, this should do the trick:
jqueryElem.click(myHandler.bind(this,[a,b,c,d]));
Not having the array might cause b to be treated as an eventHandler. Notice I used an array, but it could also be an object, for instance.

Simple event system using JavaScript (ES5) - JS Custom Event

Instructions:
make this code work without modifying snippet it in any way.
Use only plain old JavaScript and no third-party libraries.
Write new code that enables the code below to work properly.
Hint: Feel free to extend native objects... even though it's typically a bad practice.
// Start with an object, any object
var myObject = {};
// Register an event on your object using
// an `on` method
myObject.on('myEvent', function(data) {
// Log the data passed to the callback
console.log(data);
});
// Trigger the event using a `trigger` method.
// Include some data when you trigger the event.
myObject.trigger('myEvent', {
company: 'ABC Corp',
location: 'WTC Bangalore, IN',
website: 'http://abc.co'
});
// Register a different event
myObject.on('yourEvent', function() {
console.log('yourEvent fired');
});
// Trigger the new event
myObject.trigger('yourEvent');
// Trigger all existing events using a special
// "star" identifier.
myObject.trigger('*');
// Remove one event by name
myObject.off('myEvent');
// Since we've removed the event, this should
// do nothing
myObject.trigger('myEvent');
// Remove all existing events
myObject.off();
// Since we've removed all events, this should
// do nothing
myObject.trigger('*');
Everything else went well. I'm unable to get "arguments" while implementing myObject.trigger("*"); unable to read arguments object / parameters while implementing "*" and hence throw undefined.
My JSFiddle
Disclaimer
I obviously dont know what school you go to or anything, but please don't fool yourself trying to fool your teachers. With a few simple questions they'll know if you understand the material or not, and if you show up with a good answer but no knowledge to back it up, they will know what's up. I'm not accusing you of this, just a friendly word of advice of someone who has had good connections with his teachers after graduating last year ;)
So, how do we do this? Basically, you will have to add some functionality to the prototype of object, at least if you want this to affect all objects made afterwards. You can always create your own class and add the function to that prototype if you only want that class to have this functionality.
We need 3 functions added to the prototype, on, off and trigger of course. On top of that we add one extra property called events, initially an empty object.
You can look at the raw code for all these in the jsfiddle, I will only go through the structure and logic of the code here.
events will hold all the handlers (functions) associated with each event. When adding an event for the first time, we add a eventName property to the events object, the value for this property is initially an empty array.
on will find (or create) the array linked to eventName in events, and push the function into the array (note we do not call the function at this time, we simply store the reference to the function in the array).
off will iterate the array of eventName, and if it finds the same function (note the ===), remove it from the array.
trigger will iterate the array of eventName and call each function. Note that the function is called with the this keyword in the function set to the object, and with the same parameters as the trigger function was called (except eventName, the first parameter, which is filtered out). Yes that means you can pass as many parameters as you want to trigger(), and they will all be passed to each handler.
I won't go into detail what things like splice, slice, ===, arguments and apply do exactly, I'm sure you can find more and better information about that elsewhere on the world wide interwebs.
There's a lot more you can do for this, like making the events object invisible through some nice uses of scoping, but that wasn't part of the question so I didn't bother with that.
If you have any more questions after looking through this, feel free to ask. I also didn't test it extensively so if you find any bugs, let me know.
EDIT: I didn't read through the comments at first, but I now also added support for the '*' wildcard. Basically the functions now check for the wildcard and will iterate all eventNames on the event object when removing or triggering. You can also remove all functions for an event by not giving a function or by giving the same wildcard, but with an eventName.
EDIT2: had some bugs running the teacher's code, realized I forgot to check for hasOwnProperty while iterating. Look that one up, it's very important when working with prototypes!
I now put in the teacher's code in my jsfiddle, to show you that it works :)
jsfiddle with own code
jsfiddle with teacher code
EDIT3 - about the 'undefined' log.
The teacher's code calls .trigger 5 times, and you should see 4 console logs and as far as I can tell, they are all correct.Let me run through each trigger, and the subsequent console logs.
You add a handler to myEvent, which logs the first parameter
You trigger myEvent, with parameter => The parameter (the object), is
logged.
You add a handler to yourEvent, which logs a hardcoded
string.
You trigger yourEvent, no parameter => The hardcoded string is logged'
You trigger * with no parameter, all handlers run => undefined is logged, since no parameters were given, data in myEvent's handler is undefined. The hardcoded string is also logged
You remove the myEvent handler, trigger myEvent and confirm no functions are called
You remove all event handlers, trigger * and confirm no functions are called from any events.
I honestly don't know what you expected to happen on step 5, since you give no parameter, the data is assigned undefined, that's intended behaviour.
If you want to merge the data given in step 2 so it remains on the object, then instruct so in your handler. (for example, iterate all properties of data and add them to this, then log this). Right now you simply pass it data, it gets logged, and then thrown away. You can also add a parameter in step 5, and then all handlers will receive it (including the yourEvent handlers, but that one doesn't assign nor use it).
document.getElementById("myBtn").addEventListener("click", displayDate);

about javascript event handler, it works weird

I am a beginner in javascript. and have no experience in programming, at all.
So I'd like you to be generous to beginner.
And here is my question.
I'm trying to code javascript unobtrusively.
So I put in all of my js codes into external js file. for example : test.js
and deleted these codes. to do unobtrusive js coding. for example :
and I tried to use these 2 methods :
variable.onclick=test(arg1, arg2);
variable.addEventListener('click',test(arg1, arg2),true);
but these triggers didn't work.
to put it delicately, function test(arg1, arg2) worked right after dom loding finished. regardless of activating 'click' trigger.
So I spent several hours solving this problem, and finally got a solution. this is it.
variable.onclick = function(){
variable.addEventListener('click',test('arg1','arg2'),true);
}
I wanna know why first two methods didn't work, and why that solution works well.
I solved the problem, but don't know why, and how...
In JavaScript, when you reference a function by name and follow that reference by a parenthesized list of arguments, that means that you want to call the function, right then and there. Thus a statement like
variable.onclick=test(arg1, arg2);
will assign to the "onclick" property the value obtained by calling the "test" function. In other words that statement means
Please call the function "test" passing it "arg1" and "arg2", and assign whatever it returns to the "onclick" property of the object referenced by "variable".
An event handler must be a function, however, and your "test" handler probably returns either nothing, or something that's not a function. So it didn't work.
Your solution, however, is also incorrect. You're successfully assigning a function to the handler property, but your function is itself installing another event handler. There's no reason to do that here, and in general setting up event handlers from within other event handlers is a suspicious practice. All you need is:
variable.onclick = function() { test(arg1, arg2); };
variable.onclick requires a function declaration by design. In your case you could have just done
variable.onclick = function(){
test(arg1,arg2);
};
The way you did it won't work because you're not giving the click handler any instructions. The corrections I have made say that when the variable (the one with the click handler attached) is clicked trigger this function that will in turn trigger the test function.
Same thing goes for the second one
variable.addEventListener('click', function(){
test(arg1,arg2);
});
This works again because you are saying when this variable is clicked run the function that will trigger the test function.
Basically you are trying to assign the result of running a function, the test function as a task for the click handler to run. This won't work except maybe your test function returns a function that contains code that you want to run when the click event is triggered. Hope this helps.

Javascript event handler order

I have an input field, which has two event handlers bound to it.
Validate & AutoSave
Obviously I want to validate before I save. If validation fails, the "invalid" class is added to the input and autosave will check for that class before it proceeds.
This works well enough, but is there a way to guarantee Validate runs before Autosave in all cases?
If you use JQuery to bind your events, it guarantees that handlers are fired in the same order that they were bound. Otherwise the order is officially undefined.
If you cannot use JQuery or a similar framework you can easily simulate this by using your own custom even binding, where your generic handler is a function which keeps an array of functions and calls them in order.
Normally you'd have the Save event handler call Validate() which will return true if everything is fine and ready to be saved.
function onSaved() {
if (!validate()) {
// set class
return;
}
// do the save
}
Why not attach just one handler -- Validate -- and call AutoSave from inside it?
For an answer to your question that isn't also a question, see this post or this one or this one.
Already answered - but just to add this piece of knowledge, the order of event handlers can not be relied upon. It may in any given implementation be predictable, but this can change from one (Javascript) implementation to the next and/or over time. The only thing certain is that they all will be executed - but not in what order.
Note that the situation is similar when there is an event handler for a DOM object and another one for the same event for a child or parent - which of those is executed first is not always clear as well. See http://www.quirksmode.org/js/events_order.html

Categories