theboywhocriedWoolf | Tal Woolf

JavaScript Event Manager


Rule those unruly events

When working on small-scale projects, applications or experiments I prefer to take on a ‘do it yourself’ attitude and try to leave third party libraries well alone wherever possible.

This may be a daunting thought at first but it will give you a more intricate knowledge of how things work. Allowing you, when the right time comes, to fully enjoy the drive of using libraries with a more appreciated understanding from having walked without them.

With this in mind I created the EventManager.js. As well as simplifying the process of adding events the Event Manager also takes care of the following:

- Cross browser event handling to support all major browsers

- Device orientation, calculating alpha beta and gamma positioning for both “DeviceOrientation” and “DeviceMotion” while automatically re-adjusting properties based on “window.rotation

- Automatically switching between mouse and Touch events

- Calculates DOM elements X and Y offsets to return correct mouse and touch positioning

- Calculates intersecting touch points to automatically prevent touch events from firing if the touch position is out of the DOM’s bounds

- Custom event dispatching

In the Beginning…

I started off creating a simple event management class to deal with the positioning offsets for mouse events when interacting with DOM elements on the page.  However, after a few requirement changes and various projects later I soon found that the event class had grown before my very eyes and was now big enough to venture alone into the world.

The Manager uses a Hash Mapping style system and stores all events and handlers as Key Value pairs.  When a listener is added the Manager first checks to see if that DOM element has been used before. If this is not the case it creates a new slot for it in a handlers array.  Next the Manager checks to see if that event type has previously been paired with this element, again, if not it creates a new array using the event type as its Key neatly filing away all associated handlers.

Managing events in this way also allows you to add multiple listeners whilst only needing to register the listener once with the DOM element. Rather than adding the listener each time the Event Manager detects an already existing event type for that DOM element and simple files the new handler into its array, looping through and calling each handler upon the events firing. This makes for a neater implementation and smaller clean-up, consequently reducing any potential memory leaks.

Why cant we all just get along?

When adding or removing listeners to a particular element browser differences can cause a few issues. To combat this I have implemented John Resig’s handy code snippet that nicely adds and removes events to a DOM element independently of the browser, firstly detecting their availability and then adding or removing them.

Be cautious when adding ”deviceorientation” and “devicemotion” events. The Event Manager is successful in automatically switching to the available type, with ”deviceorientation” being preferred. However, Firefox for desktop shows both DeviceOrientationEvent and DeviceMotionEvent as available, which causes complications as in reality only DeviceMotionEvent works. So if you know you are planning to use device orientation events for desktop browsers consider adding in some browser detection code of your own, or alternatively stick with ”devicemotion” events.

Are you a Man or a Mouse?

One of the main reasons for my creation of the Event Manager was to try and curb my annoyance of having to switch between mouse events and touch events. Whether you are creating or even considering mobile, which I assume most people are, it’s not always easy to constantly test on device so having to register for one type of event then another or even both was starting to grind my gears. To combat this I have implemented a method within the Event Manager that automatically uses touch events (if available) taking away your need to even consider what events you should listen for.

The Manager Exposed

The class is pretty self explanatory and I have commented on most of the code but to get you up and running a little faster I will briefly describe the available methods and what they do. Because lets be honest reading the manual may not be as macho but it will get us there faster.  Having said this, when you have a little more time on your hands, I am firm believer in taking a look under the hood for yourself.

Adding and Removing Listeners -

// adds an event listener - for custom events omit DomElement by
// nullifying DomElement
EventManager.addListener( DomElement, EvntType, Handler, OptionalID )
// removes an event listener from DOM element - to remove custom
// events, omit DomElement by nullifying
EventManager.removeListener(  DomElement, EvntType, Handler )
// alternatively Add or Remove at once
EventManager.captureEvents( DomElement, downHandler, moveHandler, upHandler, overHandler, outHandler, AddOrRemoveBoolen );

addListener - adds a listener to a DOM element. If an element is undefined the Manager presumes that this is a custom event that will be dispatched using “EventManager.dispatch” (described later on). The “id” parameter is optional and allows you to append a unique ID to that element if you have enabled the “useUniqueID” property.

removeListener - removes a listener from a DOM element, if the element is not defined then the Manager will presume this is a custom event.

captureEvents-  allows you to add and remove multiple listeners at once by passing in the required handlers and nullifying the ones you don’t need. This method also automatically switches between touch and mouse events. The last parameter dictates whether or not to add the listeners or remove them from the Dom element passed. For the best results dont mix and match methods when adding or removing listeners, unless you are fully aware of the event types used, for example “captureEvents” uses “mousedown” instad of “click” when adding mouse events so trying to then remove a click event using “removeHandlers” will not work.

Automatically add Mouse and or Touch Listeners -

* autoAddListener must be used in conjunction with autoRemoveListener
* to work correctly - CANNOT be used for custom events -
* */
// adds a listener automaticaly switching the passed event type
// to its equivalent mouse or touch type.
EventManager.autoAddListener( DomElement, EvntType, Handler, OptionalID );
// remove listener
EventManager.autoRemoveListener( DomElement, EvntType, Handler );

autoAddListener - automatically switches your event types to match the availability of device, altering the way the Manager deals with the dispatched events. So “click” or “mouseDown” will become “touchstart” and so on but just remember you are only limited to available touch events (touch, move, end).

autoRemoveListener - removes a listener automatically switching to the available event types.

Swap Handler -

// swaps out existing handlers
EventManager.swapHandler( DomElement, EventType, CurrentHandler, NewHandler );

swapHandler - allows you to swap out old handlers with new handlers at runtime without having to add then remove any listeners. For custom events, omit the DomElement by nullifying.

Dispatch -

// dispatch custom events to all registered handlers
EventManager.dispatch( EventType, args );

dispatch - will dispatch to all handlers you registered without a Dom element paired with that event type passing in any required paramaters.

Dispose -

// dispose the EventManager removing all stored data

Use a unique ID and use touch bounds properties -

// defaiult - false
EventManager.useUniqueID( Bool );
// default - false
EventManager.useTouchBounds( Bool );

There are two main methods when setting up listeners within an application, the first adds a listener to a parent DIV, Body or Window and then based on the mouse position it determines which element was interacted with. The other is to add a listener to each individual element itself. The problem with the latter is that unless you specify an ID ij the HTML markup all elements come through with the same object reference, such as DIV. To allow you to add listeners to individual DOM elements such as DIV’s enable useUniqueID which will either automatically append an ID to each new element passed into the Manager or append the ID you specified when adding a listener.

useTouchBounds - if true prevents an event from being dispatched when a touch point is no longer within the target DOM element

Returns if you please..

Touch, Mouse and Orientation events return different properties within the objects that are dispatched to their registered listeners:

Mouse - { x, y, uniqueID: ( id of the DOM element ), event }

Touch - { x, y, isPressed, id : ( id of touch ), event, uniqueID :  ( id of the DOM element ) }

Orientation - { gamma, beta, alpha, cssRotation, absolute , event }

That’s All Folk’s

So that’s it, the EventManager in all its glory. If you have any issues, suggestions, alterations or improvements of your own I would be happy to hear about it. Feel free to grab the full fat EventManager or the diet minified version. Alternatively Spoon me on GitHub.

Leave a Reply

(* Required)