Module-documentation

version 1.0.0
module: event-dom version 0.0.1
size-min gzipped: 5.78 kb (incl. dependencies: kb)
dependencies: polyfill/polyfill-base.js, js-ext/lib/function.js, js-ext/lib/object.js, utils, itsa-event
maintanance: Marco Asbreuk
home


all modules

DOM Events

The event-dom module integrates DOM-events into the event-module. Using this module, you have all the power of the event-module applied to DOM-events.

event-dom touches no single dom-node. Listening to events always happens by listening at the capturephase of document. Subscribers can be set without the need of node's being part of the dom.

The loaderfiles combine event, event-dom and event-mobile all into ITSA.Event.

Getting Started

With nodejs:

The module event-dom is a typical browser-module.

In the browser:

For browser-usage, ITSA has a predefined loaderfiles. Once included, a global ITSA object with default features is available. For customized loaderfiles, read: Customized build.

<script src="/itsabuild-min.js"></script>
<script>
    var Event = ITSA.Event;
</script>
When using your own customized build-file, this module-requirement needs to be invoked with the window-object.

Features

This module brings DOM-events to a higher level:

The Basics

After including this module, you can listen for DOM-events, just like listening to other events. The difference with other events is that DOM-events don't need an emitterName when listening:

Example: listening to DOM-events

var showMsg = function(e) {
    // e.target is the node that was clicked
    alert(e.target.innerHTML);
};

Event.after('click', showMsg, '#buttongo');

When listening to DOM-events, you always need to pass the filter-argument, this is a css-selector by which you tell what nodes you want to listen at. It doesn't matter if those nodes are in the dom yet, or at any later time.

DOM-events

This module handles both custom-made events as well as DOM-events. Both get the convention customEvents and they are treated as the same. In reality, DOM-events are caught at their capture-phase and transported into this Eventsystem. It also looks at the response of this Eventsystem: if the customEvent is halted or defaultPrevented, then the original DOM-event will be defaultPrevented. This way we keep DOM-events and our Eventsystem completely separated.

There are some differences between DOM-events v.s. Custom created Events that you should be aware of:

Behaviour DOM-events

Note1: Because DOM-events should be listened through delegation, never directly on a DOM-node, you cannot use code like domNode.on('event', callback)_. Not being able to do so, leads into a better usage, for you'll never run into issues with creating listener on objects that don't exist.

Note2: Mobile-events are part of the system as well, by integrating Hammer.js.

Listening to DOM-events

Because the listener-helpers are not part of node's, you should listen to DOM-events through Event or any instance that has the listener-helpers. Which one you choose is up to you, the difference is that the object which listens for the event will become the context inside any subscriber.

listening to all click-events

Event.after('click', function(e) {
    var buttonnode = e.target;
    ...
}, 'button');

listening all click-events of buttons inside container

Event.after('click', function(e) {
    // this code only gets invoked when a button inside #container was clicked
    var buttonnode = e.target;
    ...
}, '#container button');

halt and preventDefault

e.halt() and e.preventDefault() are methods that can be invoked inside before-subscribers. These methods change the event-lifecycle:

halt the event inside the before-subscriber

var beforeClick = function(e) {
    e.halt();
    // stop the event --> no defaultFn and
    // no after-listeners get invoked
};
var afterClick = function(e) {
    var node = e.target;
    // the code will never come here
};
myMembers.before('click', beforeClick, '#buttongo');
myMembers.after('click', afterClick, '#buttongo');

e.preventDefault() will prevent any default-action that comes with the DOM-event. It depends on the DOM-event itself what that default-action would be. For example: anchor-elements will navigate to their href by the default-action. Also form-submission has its submission done by the default-action.

stopPropagation and stopImmediatePropagation

e.stopPropagation() and e.stopImmediatePropagation() are methods that only apply to dom-events. When an event on a DOM-node occurs, it bubbles up the dom tree, to its parentNode (which receives the event), to its parentNode etc, right up to document. There might be situations where you want to halt this bubble-behaviour. In that case you should call e.stopPropagation() at a beforesubscriber on one of the nodes inside the bubblechain. Any subscribers higher up the dom-tree will not get invoked. Any other subscribers at the same node do get invoked. Here lies the difference with e.stopImmediatePropagation, which also prevents any other subscribers at the same node.

Both e.stopPropagation() and e.stopImmediatePropagation() do not preventu> the default-action of the event. See DOM stopPropagation for an example of stopPropagation and stopImmediatePropagation.

Filtering

The default way to sepcify the target you are listening to, is by defining a selector, which is done at all the examples above. The benefits here is that e.target wil be the DOM-node that matches this selector. Another way is by specifying your own selectorfunction. This function should return true on the node that gives a match.

Note 1: When you use your own selectorfunction, e.target will equal the deepest DOM-node that initiated the event, not the node that matches the filterfunction. In this way, using a css-selector instead of your own filterfunction is highly preferable.

Note 2: Do not reset e.target inside your own filterfunction. There might be more listeners to the same UI-event: resetting e.target will mess things up.

example using a filterfunction

Event.after('click', function(e) {
    // button "#buttongo" is clicked,
    // e.target could be an innernode of button, like a <i>-tag
}, function(e) {
    return e.target.id==='buttongo';
});

Available events

All DOM-events that the browser emits will come through to the Eventsystem. See the list of available dom-events here.

You should be carefull when it comes down to gesture-events (touch- or pointer-events). These are uses on mobile devices. If you want to subscribe to those events, you should use the module event-hammer

Beside native DOM-events, this module enriches some events and adds some custome events for a richer experience:

click becomes tap

There is no click-event anymore!!! Because new browsers support the tap-event (which is quiker --> a click-event delays for 300ms on mobile), we basicly got 2 of the same events: click and tap, where one performs better. That's why we keep it modern: no click-event will occur only the tap-event.

centerclick and rightclick

The native click- event is devided into: into: tap, centerclick and rightclick. This means that -when subscribed to a tap-event- the subscriber only gets invoked on a left-mouseclick or a true tap-event when on mobile devices. And on systems where a mouse is present, you can listen for a rightclick or centerclick-event.

anchorclick

Because the click-event isn't available, you need another way to listen (or preventDefault) anchor-clicks. Clicks on anchor-elements are transformed into the anchorclick-event.

mouseover and mouseout

Native DOM way

By nature, these event are noisy. That is, suppose you have this structure:

<div id="hover-me">
    <div class="header">
        <button class="close">Close</button>
    </div>
    <div class="body">
        ... more markup
    </div>
</div>

Then a mouseover subscription on #hover-me would be called three times when a user moved the mouse over the close button because

Using event-dom module

This module doesn't work that way. By nature it will only call the subscriber when there is a match by the selector. IE introduced mouseenter and mouseleave that do the same. This module has no mouseenter not mouseleave support: you can just use mouseover and mouseout they just work.

outside events

All DOM-events can be appended with outside. This means that the original event will be listened to, but the subscriber gets invoked when the css-selector is not matched.

Example: listening to outside-events

var showMsg = function(e) {
    // e.target is the node that was clicked
    alert(e.target.innerHTML);
};

Event.after('clickoutside', showMsg, '#somediv');

Note1: you can only use outside-events when you are using a css-selector as filter. Because the eventName internally will be translated into its 'non'-outside- variant, you will expect wrong rsults when passing a function into the filter.

Note2: with outside-events, e.target matches the most innernode where the event occured. It does not match any element of the css-selector for it didn't match those elements.

Additional events

This module comes with some handy additional events non-native DOM-events that might be handy to use:

hover

mouseover and mouseout subscriptions are very common used to create an effect that only lasts as long as the mouse is over an element. To make that easier, there is the hover-event. The subscriber gets invoked on mouseover and receives the eventobject which has the property e.hover which is a Promise. You can use this Promise to get notification of when mouseout happened. The Promise e.hover gets resolved with relatedTarget as argument: the node where the mouse went into when leaving a.target.

You best subscribe to the after hover-event: its subscriber gets invoked once hover started.

Example: listening to DOM-events

var showMsg = function(e) {
    var node = e.target;
    node.innerHTML = 'Mouse entered';
    e.hover.then(function(relatedTarget) {
        node.innerHTML = relatedTarget.id ? 'Went to '+relatedTarget.id : '';
    });
};

ITSA.Event.after('hover', showMsg, '#container');

valuechange

valuechange emits when the value property of an <input>, <textarea>, <select>, or [contenteditable="true"] element changes as the result of a keystroke, or mouse operation.

Programmaticly changes should be done through HtmlElement.setValue() which is provided by dom-ext. This method ensures the valuechange event will be fired (as long as the module event-dom/extra/valuechange.js is loaded).

This adresses changes inside these elements like:

The valuechange-event provides more reliable input notifications and should be used when you want to be notified about changes to these type of HtmlElements.

focusnode

The focusnode event happens when the focus gets on any element inside the specified element (or the element itself). When the focus switches between any descendant elements, the focusnode-event won't fire again. The focusnode event can be used for any node, even when the node itself can't get focus. This is a convenient way for using with container-nodes.

blurnode

The blurnode event happens when the focus is not inside this node anymore. You cannot listen for the blur-event, because blur is only available for focussable nodes. Suppose you have a container-node with two input-elements. When the focus changes from input#1 to input#2, then input#1 and container-node will recieve the blur-event. But the focus still lies inside a descendant-node of the container, so blurnode won't happen. When the focus is set on an input outside the containernode, blurnode will be emitted.

By listening to blurnode, the subscriber will only be invoked if the focus really gets out of the node.

Note you might thing that the focusoutside-event could be used as well, but there is a small difference: when the browser looses the focus, there won't be a focusoutside event. But there will be a blurnode-event.

Properties of eventobject

All DOM-events receive an event object that extends the eventobject created by Event (bold properties are generated by our Event-system):

Name Value
target DOMnode that received the event.
type eventName. Like `click`.
emitter emitterName of the emitter. Will always be 'UI' because it is a DOM-event
returnValue Returnvalue of the default-function
status Object holding the status about what happened to the event. Has the following properties:
  • e.status.ok --> `true | false` whether the event got executed (not halted or defaultPrevented)
  • e.status.defaultFn _(optional)_ --> `true` if any defaultFn got invoked
  • e.status.preventedFn _(optional)_ --> `true` if any preventedFn got invoked
  • e.status.halted _(optional)_ --> `reason | true` if the event got halted and optional the why
  • e.status.defaultPrevented _(optional)_ --> `reason | true` if the event got defaultPrevented and optional the why
  • e.status.propagationStopped _(optional)_ --> `DOMnode` the DOMnode on which propagation was stopped
  • e.status.immediatePropagationStopped _(optional)_ --> `DOMnode` the DOMnode on which immediatePropagation was stopped
  • e.status.unSilencable _(optional)_ --> `true` if the event cannot be made silent
any other specific dom event-properties

Known issues

When using a filterfunction at a before-subscriber, no additional properties can be set at the eventobject. This is only the case with beforesubscribers: aftersubscribers can do so. Also, you can manupilate the eventobject in the before-subscriber (only filterfunctions suffer this behaviour). This behaviour is different from custome-events, which always allow setting properties at this stage.

Compatability

API Docs