Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firing and capturing custom events

Imagine this scenario (it's really just a scenario):

  • I have a global counter that gets incremented on every mouse click.
  • when I reach 50 clicks, I want to fire a custom event named 'reachedCount'
  • I want to register my window object to capture that event with something like
    window.addEventListener('reachedCount', function(args){alert(args.count);}, false)

So, my problems are that I don't know, and can't find anywhere how I can pass arguments to my eventHandler. Also, I had tried the method Rikudo posted, but it doesn't work in IE lt 9.

Is this possible? How?

like image 531
André Alçada Padez Avatar asked Aug 14 '11 13:08

André Alçada Padez


People also ask

What are the two types of custom events?

Custom events can be created in two ways: Using the Event constructor. Using the CustomEvent constructor.

What is the purpose of custom events?

Custom events offer a new way of thinking about your code: they put the emphasis on the target of a behavior, not on the element that triggers it.

How do you capture an event?

For event capturing, we set the handler capture option to true: elem. addEventListener(event, handler, true) . By default, it is set to bubbling: false . In the example below, we add click event handlers on every HTML element using JavaScript for loop.


2 Answers

Using Rikudo Sennin's answer, you can pass parameters to your event handler by putting them inside the event itself, just like the DOM handlers do!

function fireEvent(name, target, param1, param2) {
    //Ready: create a generic event
    var evt = document.createEvent("Events")
    //Aim: initialize it to be the event we want
    evt.initEvent(name, true, true); //true for can bubble, true for cancelable
    evt.param1 = param1;
    evt.param2 = param2;
    //FIRE!
    target.dispatchEvent(evt);
}

function foobar(ev) {
    alert("foobar" + ' ' + ev.param1 + ' ' + event.param2);
}

function testEvents(param1) {
    window.addEventListener("foobar", foobar, false); //false to get it in bubble not capture.
    fireEvent("foobar", document, 'test', param1);
}
like image 197
HBP Avatar answered Nov 04 '22 02:11

HBP


All your arguments -just like all the other information about the event- should be carried inside the Event Object itself. The completely self-contained Event Object carries all the needed information about that event. Your argument values are generally set when the new custom event object is created (not when the event is fired).

(All the fragments of example code below exactly match all the other fragments. In fact, some of the examples may not make much sense by themselves; whenever this happens refer to previous example fragments.)

For your own arguments, in some cases you could reuse existing fields (or perhaps even add new fields of your own):

var newEvent = ...
newEvent['scrollX'] = your-own-custom-value;

Exactly how these would get set differs depending on whether you're able to use the new standardized HTML5 way, or must fall back to older browser support. (The various "shims" that add custom event support even to old browsers that don't provide anything at all aren't covered here -- many of them will provide their own rather unique way to set arguments.)

The HTML5 way involves the "dictionary" (second) parameter to the object constructor, something like this:

var newEvent = new CustomEvent('customname', { propertyname : propertyvalue,
                                            anotherpropname : anotherpropvalue,
                                              thirdpropname : thirdpropvalue,
                                                etcpropname : etcpropvalue }   );

The corresponding older method would look something like this:

var newEvent = document.createEvent();
newEvent.initEvent('customname', true, true);
newEvent['propertyname'] = propertyvalue;
newEvent['anotherpropname'] = anotherpropvalue;
newEvent['thirdpropname'] = thirdpropvalue;
newEvent['etcpropname'] = etcprovalue;

(The above example may also make it clearer just what the HTML5 CustomEvent constructor is actually doing.)

Using existing property names like this (or creating your own properties:-), isn't generally recommended though, because cross-browser issues and debugging hassles can be pretty severe. Once in a while this will be necessary and it will work, but don't rely on it as a general technique. Even though some types of event objects include a particular named property, similar types of event objects may not. Some event properties may be "read-only". Event objects are internally highly variable from one browser to another and even between browser versions. And creating your own new properties may confuse the browser's implementation of Javascript.

Instead, use the one particular property that's "set aside" for your use in custom events and nothing else: detail.

Often you will have several arguments, yet there's only one property set aside for your use. So the conventional approach is to always make all your arguments into just one "object", something like this:

var myargs = { my : 1,
              own : getElementById('foo');
             args : { X : 32, Y : 53 }    };

The HTML5 way of setting this variable will look something like:

var newEvent = new CustomEvent('customname', { bubbles : true,
                                            cancelable : true,
                                                detail : myargs } );

The older interface for doing the same thing will look something like:

var newEvent = document.createEvent();
newEvent.initEvent('customname', true, true);
newEvent['detail'] = myargs;

(Of course if you make heavy use of the Javasript "literal object" curly brace syntax to minimize your typing, your code may look a little different than the above examples which prioritize clarity.)

(Two existing event properties, 'bubbles' and 'cancelable', must always be set for every event, regardless of setting possible custom arguments. If the new HTML5 way is being used, they will always appear as two additional lines in the object that's the second parameter to the CustomEvent constructor. If the older way is being used, they will be the second and third parameters to the initEvent(...) call.)

Two different methods of triggering the custom event are also provided. The newer HTML5 way uses object.dispatchEvent(newEvent). The older way uses object.fireEvent('customname', newEvent, false). Here "object" means DOMObject/Element; exactly what (if anything) happens if "object" is something besides a DOM Element is even more browser-specific than the rest of this topic. (Mixing the HTML5 way and the older way generally works, but can be confusing. Another frequent source of confusion is having a system function named fireEvent(...) and also defining your own function with that same name.)

(There are some arcane differences between the two ways of triggering a custom event. The older fireEvent(...) method requires you to re-specify the name of the event even though you already specified it in the initEvent(...) call. And the older fireEvent(...) method does not trigger the "default action" [whatever that means].)

On the other side, custom arguments are accessed the same way regardless of whether the HTML5 or the older method of setting up the event was used. It will look something like this:

function customhandler(evt) {
        alert(evt.detail.own);

Where some of your custom values are in fact objects themselves, the dot notation can get so long it may look like a typo ...but it's not. For example:

function customhandler(evt) {
        alert(evt.detail.args.X);

It appears some of this may work slightly differently in IE 9 and below, though. Hopefully the problems are simply the usual ones with attempting to re-use -or even create- properties of an event object. If problems are more pervasive, you could put a "sorry:-(" message on your website, or you could wait for IE6/7/8/9 to die, or you could try to cross-browser hack it yourself, or you could use some sort of shim/fallback. It's not clear to me whether it's better to find a shim that "looks exactly like" the conventional interface, or to use the alternate interface provided by the shim for everything (even when the conventional interface is available).

(Disclaimer: Of course I could be wrong about some of the above...:-)

like image 28
Chuck Kollars Avatar answered Nov 04 '22 04:11

Chuck Kollars