- Notifications
You must be signed in to change notification settings - Fork1
Rich, declarative[, custom] event handling
License
MIT, MIT licenses found
Licenses found
esha/Eventi
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Powerful application events and event handling, made easy.
Download thefull,minified, orserver versions.
Bower:bower install eventi
NPM:npm install eventi
Component:component install esha/Eventi
Creates and dispatches the specified event(s) on the specified target(s), attaching any specified data.
Arguments | Required | Type(s) | Default | Description |
---|---|---|---|---|
target | No | Object,Array | global/window | A single target or array of them. |
eventi | Yes | String,Event,Array | One or more space-delimited Eventi definitions, an Event instance, or an array of either (in which case youmust specify a target argument). | |
data | No | Any number of arguments that will be put in an Array and attached to all created events as thedata property. |
*When passing events in an array, youmust specify a target argument.
Any attached data will be fed to the event listeners as additional arguments after the event itself and any data registered along with the listener itself.
Eventi.fire(user,'account:close',accountId);
Registers the specified function as an event listener for the specified event(s) on the specified target(s).
Arguments | Required | Type(s) | Default | Description |
---|---|---|---|---|
target | No | Object,Array | global/window | A single target or array of them. |
eventi | Yes | String,Array | One or more space-delimited Eventi definitions or an array of them (in which case youmust specify a target argument). | |
fn | Yes | Function | The function to be called when a matching event hits the target(s). | |
data | No | * | Any number of arguments that will be passed directly to the listener function as arguments (after the event argument). |
The function argument will be called with the target as its context, the received event as the first argument, and data arguments registered with the listener as the next arguments, and any data arguments attached to the event itself after those.
Eventi.on(user,'account:open',function(e){console.log('Event: type='+e.type+' category='+e.category,this/* will be user */);});
Overloading ofon()
for convenient registration of multiple listeners at once. Iterates over the object argument, registering key/value pairs as eventi/fn pairs.
Eventi.on(user,{'account:open',function(e){console.log('Event: type='+e.type+' category='+e.category,this/* will be user */);},'account:close',function(e,accountId){console.log('User: '+this.id+' is closing account '+accountId);}});
Registers event listeners on the element that search between an Event's target and the listening element for an element with a handler attribute (e.g.data-click="..."
) telling the listener how to handle the event.
Attributes | Required | Content | Description--------- | -------- | ------- | ------- | -----------eventi, data-eventi | For all events exceptclick
| Space-delimited Eventi definitions | Defines listeners for an element.{alias}, data-{alias} | No | Element function, global function, Eventi definition | Either a reference to the function to be called, an Eventi definition to be fired on the element, or nothing, in which case the alias is used as the content.
<divid="admin"data-eventi="/beforeunload=>save keyup[delete]<.account>=>delete"><ulclass="accounts"><liclass="account"tabindex="0"delete="account:close">Savings</li> ...</ul><buttonclick="account:open">New Account</button></div>
Unregisters event listeners that match the specified event(s) and/or function on the specified target(s), or the global/window object, if none is specified.
Arguments | Required | Type(s) | Default | Description |
---|---|---|---|---|
target | No | Object,Array | global/window | A single target or array of them. |
eventi | No | String,Array | One or more space-delimited Eventi definitions (or portions thereof) or an array of them (in which case you must specify a target argument) | |
fn | No | Function | The specific listener function to be removed. |
Eventi.off(user,'account:');
Defines aliases for the events specified. It creates sub-functions foron()
,off()
, andfire()
under the alias (or simple type if no alias is defined).
Arguments | Required | Type | Default | Description |
---|---|---|---|---|
context | No | Object | Eventi | An object that has been run through Eventi.fy() whose methods you want the aliases set for. |
eventi | Yes | String | One or more event space-delimited Eventi definitions. |
Eventi.alias('account:request#freeze=>hold');Eventi.on.hold(user,function(e,admin){console.log(admin.id+' is requesting freeze for '+user.id+' accounts');});//...Eventi.fire.hold(user,admin);
Convenience tool that simply defineson()
,off()
, andfire()
, making the specified target object the context and target for those functions.
Arguments | Required | Type | Default | Description |
---|---|---|---|---|
context | Yes | Object | A target object you would like to call fire/on/off upon directly. |
Eventi.fy(user);user.on('account:open',function(e){console.log('Open new account for '+this/* will be user */);});
Eventi.fy(Element.prototype);// lets you call Eventi functions on all DOM elements
Example definition:open
This is the central, "action" portion in an Eventi definition. It isnot optional in most definitions, with the exception of calls tooff()
. It is what you have left after you strip off all the other event and handler properties described below here. It is an Event instance'stype
property. This should typically be a simple verb, in present or past tense.
vare=newEventi('open');console.log(e.type);// outputs 'open'
Example definition:account:open
This is the "subject" option in an Eventi definition. The category always precedes the type and is delimited from it by a:
. It is an Eventi instance'scategory
property. This should typically be a simple noun, and represent the subject or owner of a set of events. It is good practice to provide a category for your events unless you specifically mean them to be broadly applied.
vare=Eventi.fire('account:open');console.log(e.category);// outputs 'account'
Example definition:move#up#left
These are the "adverb" or "adjective" portions of Eventi definitions. They always follow the type and are delimited from it and each other by#
. When creating an Event, they are set together in an array on the instance as thetags
property and each individual tag is set on the instance as a property with a value oftrue
. Use these to distinguish related sub-types of events or provide simple annotations for the subject or object of an event.
Eventi.on('open#new',function(e){if(e.unverified){console.log(e.tags.join(' & '));// outputs 'new & unverified'}}).fire('open#new#unverified');
Example definition:account:label(["verified","gold"])
This is the "object" portion of an Eventi definition. It follows the type and is delimited by(
and)
. The contents may be either JSON (which will get parsed), a reference to a global value (which will be resolved), or an arbitrary string. The result is an instance'sdetail
property. Use this to declaratively attach contextual information for an event at the moment it is created.
window.user={name:"jdoe123"};vare=Eventi.fire('like(user.name)');console.log(e.detail);// outputs 'jdoe123'
Example definition:cancel<.transaction>
This specifies a selector for event delegation and is delimited by<
and>
. It instructs a listener to only react to events happening within the bounds of elements matching the specified selector and to use the matching element as the context for the handling function.
Eventi.on('change<[type=checkbox]>',function(e){console.log(this.type);// always outputs 'checkbox'});
Example definition:keyup[delete]
This specifies a key or combination thereof, by either keyCode or description, and is delimited by[
and]
. It instructs a listener to only react when the event has the appropriate keyCode/ctrlKey/metaKey/shiftKey/altKey properties. Particular keyCodes may be specified explicitly (e.g.keydown[33]
), but many have been given English or symbolic equivalents for the sake of readable code (e.g.keypress[z]
). These are stored in theEventi._.codes
object and are keyup-based values, sokeydown
andkeypress
definitions shouldbe wary of those outside ASCII. The other key-related event properties are set simply by giving them-
as a suffix (e.g.[shift-13]
->e.shiftKey = true
,[meta-]
->e.metaKey = true
, etc).
The [Key] syntax is also special in that it will providekeyup
as the type for event listeners (foron()
only) that have a [Key] specified, but no type. Typically, event listeners with no type are never executed.
Eventi.on(field,'[shift-enter]',function(e){console.log(e.shiftKey,e.keyCode);// outputs 'true 13'});
Example definition:keypress[escape]@/edit
This specifies a url pattern that is matched against the currentwindow.location
to filter event listener execution.
There are three types of@
location values:
@/vanilla#string
- Will be directly matched against the URL string retrieved fromwindow.location
.@?mini={template}
- Turns the sections in braces into wildcard matches (breaking on\
,?
, and#
) whose values are gathered into a key/value object, passed as the 2nd argument to the event handler.@`reg[ular]+Exp?`
- For more complicated matching, use a regular expression. Use ` to delimit it. Handlers will receive the full match as the 2nd argument (after the event itself) and matching groups as subsequent arguments. Any handler or event data args will follow the location matching arguments.
Like [Key], @Location enables shorthand definitions by setting 'location' as the type for event listeners (foron()
only) that have a location property, but no type property.
Eventi.on('save@#file={filename}',function(e,vals,folder){console.log('Saved '+vals.filename+' in '+folder);// outputs 'Saved image.png in /pics'});window.location.hash='file=image.png';Eventi.fire('save','/pics');
Eventi also provides a unifiedlocation
event that is dispatched on thewindow
whenever the current location changes, whether viahashchange
andpopstate
events or calls tohistory.pushState
(which gets its ownpushstate
event as well). When registering a listener for alocation
event, the location is promptly tested (as if alocation
event was fired), allowing immediate execution of handlers for currently matching locations. Also, when alocation
type event is dispatched (e.g.fire('location@/path')
), the @Location will be used to updatewindow.location
viahistory.pushState
.
// assume URL is http://esha.github.io/ to startvars='';Eventi.on('location',function(e){s+=' '+e.location;});window.location.hash='hash';history.pushState(null,'push it','?push');history.go(-2);// popstateconsole.log(s);// outputs ' / /#hash /?push /#hash /'
Eventi.on('@/login',function(e,match){// will listen for 'location' typeconsole.log(e.type,match);// outputs 'location login'});Eventi.fire('location@/login');
@Location's mini-templates can work in reverse when updating the location via event. Just pass a key/value object back as the first data argument (either at listener registration or event firing).
Eventi.fire('location@?page={page}',{page:2});window.location.search==='?page=2';// true
Just to spell it out, the sum of these flexible and easy location features is a powerful event-based application router.
Example definition:_hide
This simply tells the dispatching code not to let an event propogate beyond the immediate target. Include a_
in the control characters at the start of an event definition to set thebubbles
property to false.
Eventi.on('test',function(e){console.log('Element events will never get here if they do not bubble.');});Eventi.fire(document.getElementById('hideme'),'_test');
Example definition:/login
This registers the listener on the global/window object but executes handler functions in the context for which they are registered. Include a/
in the control characters at the start of an event definition to assign the listener globally.
varbuttons=document.querySelector('button,[type=submit]');Eventi.on(buttons,'/ajaxStart',function(e){this.disabled=true;});
Example definition:^ready
Including^
in an event definition's control characters identifies it as a "singleton". The simplest way to explain these is that they are the event-equivalent of jQuery's ready() function. Once a singleton event is dispatched or received, it is remembered so that registered listeners execute no more than once and listeners registered after a singleton event is dispatched will be immediately executed with the remembered event. It's a once-for-all kind of deal. Events that go throughfire()
marked as singletons are automatically remembered for every target hit. When registering a listener for a singleton event that has not yet been fired, any matching event (marked singleton or not) will be remembered on the listener's target alone, for subsequent registrations. And yes, Eventi automatically watches forDOMContentLoaded
and fires a^ready
event on the document element.
Eventi.on({'^ready':function(){loadAsync('globalResource').then(function(resource){Eventi.fire('^resource:loaded',resource);});},'^resource:loaded':function(e,resource){// use resource here}});
Example definition:login$1
Not all event listeners are meant to last forever. You may declare a listener's end when registering it by appending a$
and a condition to the event definition. The condition may be either a number (indicating the number of executions allowed), a reference to a value (either a property of the context or global/window), or a reference to a function that will return such a value. In the case of a value, you may also prefix it with!
to reverse the condition. The "end declaration" follows all features of a definition except an alias. The most common is, of course,$1
for single-use listeners.
Eventi.on(player,'death$!player.livesLeft',player.respawn);
Eventi.on('load$1',Plugins.init);
Example definition:!location
A!
control character in the front of an event definition is only relevant when you try tooff()
it. Listeners defined as "important" may only be unregistered by a fully-matching definition given tooff()
, including the!
. This exists mostly to protect "internal" listeners (both Eventi's own and extensions) from being errantly unregistered.
Eventi.on('!demo',function(){console.log('still here!');});Eventi.off('demo');Eventi.fire('demo');// will output 'still here!'
Example definition:account:notify#SMS(balance)=>textme
Aliases are used by eitheralias()
ordata-eventi
definitions to provide a simple alias for safely and conveniently referencing event definitions (particularly complicated ones). These arestrongly recommended for any complex definitions that are repeated in your JavaScript. And, of course, fordata-eventi
declarations, they are unavoidable.
Eventi.alias('/user:logout=>shutdown');Eventi.on.shutdown(function(){// look ma, no typos!});
The following syntax options are uber-syntax for working with multiple,separate definitions. No syntax features can be shared across definitions conjoined by spaces, commas, or plus symbols.
Example definition:keyup[enter] blur
Allows you to dispatch, register, unregister, or alias multiple event definitions in the same call.
Eventi.on(editor,'keyup[ctrl-s] blur submit',function(){Eventi.fire('service:send local:save');});
Example definition:validate,save
Allows you to dispatch or listen for a connected, ordered sequence of events. This is another feature adapted fromtrigger (read this!). The concepts are the same, only the delimiter (now,
) and the API given to sequenced events have changed:
event.index
- The index of this Event in the sequence.event.previousEvent
- The preceding Event instance (if any).event.sequence
- The array of Eventi definitions in the sequence.event.pauseSequence([promise])
- Pauses the firing of the sequence. If a Promise (or other "then-able" object) is given as argument, it will automatically resume upon successful resolution of the promise. This makes async event sequences very straightforward!event.isSequencePaused()
- Returns a Boolean indicating if the sequence has been paused.event.resumeSequence([index])
- Resumes a paused sequence at either the specified index or the next index in the sequence.
Eventi.on('validate',function(e){varpromise=asyncValidate($(this).closest('form'));e.pauseSequence(promise);});
<form>...<buttonclick="validate,save,location@/home">Save and Quit</button></form>
Unliketrigger, Eventi also allows you to register listeners for event sequences as well. Such sequences can be fired as sequences (as above) or separately. You may also specify a time in milliseconds as the first data argument, in order to restrict the timeframe for sequence completion.
Eventi.on(editor,'keyup[a],keyup[s],keyup[d],keyup[f]',function(){console.log('Fake typing!');},500);
Example definition:scroll+click
Allows you to listen for an unordered group of related events before executing the handler function. This is exactly like registering a listener for a sequence of events, except that the order in which the events are received is ignored (or irrelevant).
Eventi.on(editor,'click+click+click',function(){Eventi.fire(this,'tripleclick');},200);
And yes, you can mix combos and sequences. When doing so, sequences events will be sub-events of combos, not vice versa.
- 2014-02-11v0.5.0 (alpha)
- 2014-04-03v1.0.0 (beta)
- 2014-04-04v1.0.1 (beta - IE fixes)
- 2014-04-09v1.0.2 (beta - toString and location fix)
- 2014-04-17v1.1.0 (beta - restructure artifacts, small improvements)
- 2014-04-21v1.2.0 (beta - docs, space-delimited alias arguments, artifact changes, optional
data-
prefixes, combo fix) - 2014-04-22v1.2.1 (beta - docs, shorthand type for [key] and @location)
- 2014-04-24v1.3.0 (beta - server fixes, nodeunit tests, dual Eventi ctor)
- 2014-04-29v1.3.1 (beta - jquery integration aid)
- 2014-08-15v1.3.2 (beta - swap alias/delegate parser order)
- 2014-08-18v1.3.3 (beta - fix click/enter handling in declare)
- 2014-08-19v1.3.4 (beta - properly handle form elements as targets)
- 2014-10-15v1.3.5 (beta - location event fixes)
- 2014-10-20v1.3.6 (beta - location event fix, use sourcemap friendly main file for bower package)
- 2014-10-20v1.3.7 (beta - add main entry to package.json)
About
Rich, declarative[, custom] event handling