Delegated Events and Gestures in Ext JS 5
Introduction
Prior to version 5, Ext JS was designed exclusively for use on desktop devices with traditional mouse input. As of version 5, we’ve added support for touch input as well, which allows Ext JS to be used on a larger selection of devices, primarily tablets, but also laptops with touch-screens. The implications of this change will likely be transparent to users of the framework, but it’s helpful to understand what’s going on behind the scenes. In this article, we’ll explore how the framework handles touch events and event normalization between devices.
Gestures In Ext JS
Perhaps the most exciting addition to the event system in Ext JS 5 is “gesture” events. Because the Sencha Touch event system forms the basis for the new Ext JS 5 event system, Sencha Touch users may find that they are already familiar with many of these gestures. For the uninitiated, gestures are complex events that are synthesized from lower-level browser events, for example, “drag”, “swipe”, “longpress”, “pinch”, “rotate”, and “tap”. Ext JS 5 takes touch gestures one step further and allows them to be triggered by mouse input in addition to touch input, or both, in the case of multi-input devices.
From the browser’s perspective, there are 3 basic events (start, move and end) that fire when interacting with a page using a mouse or touch-screen. The framework monitors the sequence and timing of these three events to determine when a gesture has occurred:
Browser | Start | Move | End |
---|---|---|---|
Desktop Browsers | mousedown | mousemove | mouseup |
Mobile Webkit | touchstart | touchmove | touchend |
IE10 | MSPointerDown | MSPointerMove | MSPointerUp |
IE11 | pointerdown | pointermove | pointerup |
When the framework determines that a gesture has occurred, it dispatches a gesture event to any Elements that are listening. Listening for a gesture event is the same as listening for any DOM event, for example:
myElement.on('drag', myFunction);
The following “single-touch” gestures will work across all supported devices and browsers, regardless of the input device that is used (mouse or touch):
Gesture | Events |
---|---|
Tap | tap, tapcancel |
DoubleTap | singletap, doubletap |
LongPress | longpress |
Drag | dragstart, drag, dragend, dragcancel |
Swipe | swipe, swipestart, swipecancel |
EdgeSwipe | edgeswipestart, edgeswipe, edgeswipecancel |
The following “multi-touch” gestures will work on all supported touch-enabled browsers when running on a device that has a touch-screen.
Gesture | Events |
---|---|
Pinch | pinchstart, pinch, pinchend, pinchcancel |
Rotate | rotatestart, rotate, rotateend, rotatecancel |
The Delegated Event Model
The Ext JS 5 event system makes a subtle but significant paradigm shift by moving away from directly attached DOM listeners to a “delegated” event model. This means that for each type of event (“mousedown”, “touchstart”, etc.), a single listener is attached at the very top of the DOM hierarchy (the window object). When a DOM element fires an event, it bubbles all the way to the top before it is handled. Internally, this complicates things just a bit, because the event system has to emulate event propagation by traversing up the DOM hierarchy from the target element, and dispatching event handlers, if needed, along the way. While, at first glance, this approach may seem needlessly complex, it bestows a few important advantages:
- The delegated event model is the key to being able to recognize when a “gesture” has occurred. The event system continuously monitors the timing and sequence of certain key events to detect if one of the supported gestures has occurred. It then seamlessly dispatches the gesture event in proper sequence along with the native events from which it was synthesized.
- It greatly reduces the number of DOM listeners that are attached, thus improving memory usage, and providing a single point for removal of DOM listeners. This has the added benefit of reducing the likelihood of memory leaks in older browsers, because it simplifies cleanup at window unload time.
- It allows “top-down” (capture) propagation of events in older browsers. Because IE8 does not support addEventListener() and the “useCapture” option, DOM events that are directly attached can only ever propagate using the bottom-up bubbling model. The delegated event model provides a way around this problem, by using its own artificial propagation routine, which implements both “bubble” and “capture” propagation. Users can attach capture listeners by simply passing the “capture” option when attaching the event, and event handlers will be dispatched in a top-down order across all browsers and devices. For example:
Potential Challenges With The Delegated Model
As with any fundamental change to the framework, it’s possible there will be incompatibility with existing code. As it relates to the new event system, existing code generally falls into one of two camps:
- Application code that uses only the Ext JS event system APIs to attach event listeners i.e. Ext.Element#addListener() or Ext.Element#on() will be blissfully unaware that there was a change. The new event system is designed to be 100% backward compatible with the Ext JS 4 event system, if application code uses ONLY the Ext JS APIs to attach listeners.
- Application code that mixes and matches Ext JS with other JavaScript libraries, or code that uses DOM APIs to directly attach events to elements may be negatively impacted by the switch to the delegated event system. This occurs because the timing of directly attached event handlers is now different relative to the timing of their delegated counterparts. This may cause issues if application code expects event handlers to run in a specific order, however, it will likely be most obvious when event handlers attempt to stop propagation of a given event. There are two rules to remember here:
- Delegated listeners can call stopPropagation(), and it will stop the delegated event system’s emulated propagation; however, it will not prevent any directly attached listeners from firing. The reason is that, by the time delegated listeners are processed, it is too late to prevent any directly attached listeners from firing because the native event has already bubbled to the top of the DOM.
- If a directly attached DOM listener calls stopPropagation(), it will prevent ALL delegated listeners from firing, including those on elements below it in the DOM. This is because stopPropagation() on a directly attached event will prevent the native event from bubbling to the top, thereby preventing it from being handled by the delegated event system. This also has the potential of disabling gesture recognition, because gestures are handled at the top of the DOM.
If for some reason, the delegated model is undesired, listeners can opt out using a simple flag in the options object:
However, caution is advised when opting out of the delegated event model, because it can produce the same negative effects (as described above) as for directly attached DOM listeners, especially when stopPropagation is involved.
Event Normalization
One of the primary goals of the new event system is to enable existing Ext JS apps to run on tablets and touch-screen laptops (with little or no effort required to upgrade the app). In order to accomplish this, the framework performs some basic event normalization behind the scenes. When a listener is requested for a mouse event such as mousedown or click, the framework actually attaches a similar touch event or pointer event (if the device supports such events). For example, if the application attempts to attach a mousedown listener:
myElement.on('mousedown', someFunction);
On Mobile Safari, the event system will translate it to:
myElement.on('touchstart', someFunction);
However, when running in IE11, that same listener would be translated to:
myElement.on('pointerdown', someFunction);
This allows interaction with the touch-screen to behave mostly the same as mouse interaction.
The following mouse events are translated directly to touch or pointer events when running on a touch-enabled device:
- mousedown -> touchstart or pointerdown
- mousemove -> touchmove or pointermove
- mouseup -> touchend or pointerup
- click -> tap
- dblclick -> doubletap
For some mouse events, however, there is no suitable analog in the touch world. If an application relies on any of the following events, it is up to the developer to decide if and how the feature should be implemented when using touch input:
- mouseover
- mouseout
- mouseenter
- mouseleave
While event normalization will benefit most applications, there may be an occasional need to opt out. Ext JS 5 provides the new “translate” event option for this very purpose:
myElement.on({ mousedown: myFunction, translate: false });
Setting the “translate” option to false will prevent the event system from performing any event normalization for this listener, so the handler function will only be called if a “real” mousedown occurs.
Conclusion
Ext JS has long been the leading framework of choice for HTML5 desktop apps, but now the lines between desktop and mobile are becoming increasingly blurred. The new Ext JS 5 event system, with its gestures and event normalization, positions Ext JS to continue to excel both on the traditional desktop-style device and in the new world of touch-enabled devices and browsers.
User experience matters just as much as the services a company offers. To meet this…
The latest industry reports show that around 60% of web applications rely on data grids.…
In the current age where data is wealth, almost 2.5 quintillion bytes of data are…