Multiple Components listening via eventlistener - javascript

I was wondering why you're not allowed to have multiple similar eventListeners on a single node? I imagine the case where I have multiple partials of the same type that use a single node to communicate with eachother through CustomEvents. But that doesnt seem to be working because they do all share the same EventListener and thus only one of them is able to listen and process the event.
Why is that?
Thanks!
EDIT: little code snippet:
I have
node.addEventListener("customEvent", this.func, false);
and
node.addEventListener("customEvent", this.func, false);
in two different places and this.func points to the same function but in different contextes and would eventually trigger different things. But the second listener never gets called because for some reason this something seems to assumes that their the same probably because the signature is alike or whatever.
EDIT2: I'm basically referring to this.
https://developer.mozilla.org/en-US/docs/Web/API/EventTarget.addEventListener#Multiple_identical_event_listeners

You can attach multiple similar event handlers to a node by using addEventListener.
node.addEventListener("click", function(){alert(1)}, false);
node.addEventListener("click", function(){alert(2)}, false);
Should give an alert twice.
When you assign anonymous function this works.
However you can assign multiple listeners referring to the same function and event by using bind.
var a = this.func.bind();
document.getElementById("start").addEventListener("click", a, false);
var b = this.func.bind();
document.getElementById("start").addEventListener("click", b, false);
This will alert twice. Since the function wrapper is different, it's being treated as an unique event.
You can't assign an event handler twice through node.onclick, because it's a property. When you assign it twice it will just overwrite the first handler. Event listeners were invented to cope with this.

In Java you can add multiple listeners. But, honestly, I dont like this approach. In most of the cases, you only need to trigger 1 interface/object (you are adding a overhead, logic for multiple interfaces where only one is used).
I like more the Android approach: setOn....Event (looks like JS is this way) instead of addActionListener(...) of Java.
When you need multiple triggers, you could override like:
obj.setOnEvent(function() {
doTheOtherEventTrigger();
doOneMoreEventTrigger();
...
}

Related

Multiple functions on click prevents function with event parameter - Vue

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.

Is attaching an onchange object a closure?

I have searched prior SO posts here, here and here, and couldn't an answer that made sense to me. This should be a basic question, but I'm not understanding the posts I find. They don't seem to address using a this parameter.
I want to programatically add an input with an onchange event, such that the final result is this:
<input type="button" onchange="handleButtonOnChange(this)">ClickMe</input>
I am working on a project that is using an embedded IE6 browser inside a old Delphi application, so I have to have a solution that is IE6 compatible (yes, IE6 is horrible, but there are reasons I am stuck with it for now).
My initial attempt was this:
var DaySelect = document.createElement("select");
DaySelect.id = ParentID+"-day";
DaySelect.disabled = true;
MonthSelect.onchange="handleDayChange(this);" //<--- not correct
Parent.appendChild(DaySelect);
I then read that the .onchange should be assigned an object, not a string, and one should use this instead:
MonthSelect.onchange=handleDayChange; //<--- '(this)' removed
But it seem to me that this will result in this element (notice the missing this parameter)
<input type="button" onchange="handleButtonOnChange">ClickMe</input>
If I use the line below, instead, won't this make a closure, and the 'this' will refer to the event at the time the object is assigned to the .onchange property, instead of being the event at the time of the change event?
//Does the line below make a closure?
MonthSelect.onchange=handleDayChange(this); //<-- What does 'this' refer to?
I'm a relatively new web programmer, but long time Delphi programmer. Closures still make my head hurt. I appreciate any help in this.
Also, I read here about using addEventListener and the problems with older versions of IE, and the last post on the page provides a work around. But I don't understand how it works.
EDIT -- And what about passing other parameters? It seems that many event handlers will need to have parameters specific for the attached element. It seems that it is just not possible to add a listener with any parameters.
A simple closure if you are creating the elements in JS as you show:
var DaySelect = document.createElement("select");
DaySelect.id = ParentID+"-day";
DaySelect.disabled = true;
MonthSelect.onchange=function(){handleDayChange(DaySelect);};
Parent.appendChild(DaySelect);
Since the function is created inside the scope that you create the element in, the same variables will be available to it.
EDIT:
Additional parameters can be passed with this method, for example, the anonymous function we create and attach as the handler will still have the event object sent to it:
function(e){handleDayChange(DaySelect, e);};
In the event object you will have access to the event target, but in your example the event target and "this" are not the same element, so there would be no way for the handler to know about the DaySelect element.
jQuery makes a lot of event handling much simpler which is one of the reasons many people use it, it also normalizes it's methods between various browsers so you don't have to write multiple versions of the same code (in most cases)

Events - inversion of control methods

How does listenTo / stopListening work with respect to on / off?
In the console, I'm experimenting with the Backbone event system as such ...
// works
Backbone.on('x', function(){console.log('x happened');})
// works
Backbone.trigger('x');
// works
Backbone.off('x');
// works
Backbone.once('x', function(){console.log('x happened');})
I'm trying to extend my example to use listenTo() and stopListening().
These are listed as the inversion of control types. Is there a simple way to show there use as above?
The primary difference, as viewed from the source, is that the first parameter needs to be an object.
It is exactly the same, except it's an inversion of control: listenTo binds the events on the listening object, rather than the triggering object. This is most useful for cleaning up View event handlers, because the view now knows which events it's listening to and can unbind them when it's removed. With the original system, only the triggering object (ie, the model) would have direct knowledge of the bound events.
There's a good explanation of the concept here. It shows how people used to do it before it was added to Backbone.
In your example, you don't really have a "listening" object, since your handler is just an anonymous function. But it would be something like obj.listenTo(Backbone, "x", obj.alert);, where alert would be a handler method on obj.
Because listenTo and stopListening work on a different object you need to create another object that has access to the event system. One easy way is to create a view object as such.
var View1 = new Backbone.View();
Next setup a listener:
Backbone.listenTo(View1, 'x', function(){console.log('I heard x');});
Now trigger the event on View1
View1.trigger('x');
Finally remove the listener
Backbone.stopListening(View1,'x');
That covers the 6 main methods of the event system.
Tested and working...

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

global custom events in jQuery

I want to use custom jQuery events independent of DOM elements, but I'm not sure what the best way is to achieve this.
Here's what I started out with:
// some system component registers an event handler
$().bind("foo.bar", handler); // foo is my app's namespace, bar is event name
// another part of the system fires off the event
$().trigger("foo.bar", { context: "lorem ipsum" });
After looking at jQuery's source, in particular its handling of global AJAX events, I figured this should work:
$.fn.bind("foo.bar", handler);
// ...
$.event.trigger("foo.bar", { context: "lorem ipsum" });
However, it appears that my handler function is never even called.
Am I perhaps going about this the wrong way?
If you're using jQuery >1.4 then $() returns an empty jQuery collection which would mean that no event handler is actually bound to anything. Before 1.4 it would have returned the same as jQuery(document).
It might be better to simply have a global namespace (an actual object) and then add events to that:
var FOO = {};
$(FOO).bind("foo.bar", handler);
$(FOO).trigger("foo.bar", { context: "lorem ipsum" });
I found my way here because I was looking to implement the publisher/subscriber pattern using namespaced custom events using jQuery. While the accepted solution is a way to use $.event.trigger() in a way that is not tied to DOM elements, it won't work well for a true global event implementation in a publisher/subscriber architecture (such as with a complex UI with many asynchronous actions), where you want to have arbitrary objects/elements listen for a custom event.
Through experimentation, I've found that the real answer to why AnC's events were not firing is because jQuery apparently doesn't allow the "." (period) character in custom event names...but underscores seem to be ok.
So, if you name your events something like foo_bar (rather than foo.bar), your code should work as expected. Tested with jQuery 1.4.4.
Edit: Just to be clear - I mean that periods aren't allowed for custom events if you want to use the $.event.trigger() mechanism. In scenarios where events are being triggered by objects or elements, periods seem to be ok...not sure if this is a bug or by design.

Categories