Copyright © 2013-2015 W3C® (MIT, ERCIM, Keio, Beihang). W3C liability, trademark and document use rules apply.
IndieUI: Events 1.0 is an abstraction between physical, device-specific user interaction events and inferred user intent such as scrolling or changing values. This provides an intermediate layer between device- and modality-specific user interaction events, and the basic user interface functionality used by web applications. IndieUI: Events focuses on granular user interface interactions such as scrolling the view, canceling an action, changing the value of a user input widget, selecting a range, placing focus on an object, etc. Implementing platforms will combine modality-specific user input, user idiosyncratic heuristics to determine the specific corresponding Indie UI event, and send that to the web application in addition to the modality-specific input such as mouse or keyboard events, should applications wish to process it.
See the introduction for background and usage examples. Also see the IndieUI Overview.
This section describes the status of this document at the time of its publication. Other documents may supersede this document. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at http://www.w3.org/TR/.
This document was published by the Independent User Interface (Indie UI) Working Group as an Editor's Draft. If you wish to make comments regarding this document, please send them to public-indie-ui-comments@w3.org (subscribe, archives). All comments are welcome.
Publication as an Editor's Draft does not imply endorsement by the W3C Membership. This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.
This document was produced by a group operating under the 5 February 2004 W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.
This document is governed by the 1 August 2014 W3C Process Document.
This section is non-normative.
Scripting usable interfaces can be difficult, especially when one considers that user interface design patterns differ across software platforms, hardware devices, and locales, and that those interactions can be further customized based on personal preference. Individuals are accustomed to the way the interface works on their own system, and their preferred interface frequently differs from that of the web application author's preferred interface.
In order to greatly reduce scope and achievability for a 1.0 release, the IndieUI working group agreed to remove continuous events such as the zoomrequest
event listed in the intro.
Editorial note: Need to rewrite the intro to use an example that is in scope for 1.0.
For example, web application authors, wishing to intercept a user's intent to "zoom in" on a custom image viewer or map view, need to "listen" for all of the following events:
In addition to the general user interface challenges, custom interfaces often don't take into account users who access web content via assistive technologies that use alternate forms of input such as screen readers, switch interfaces, or speech-based command and control interfaces.
It would be simpler to listen for a normalized request to "zoom in" on the current view, whereby the web application could determine the new scale factor and update its custom view accordingly. Whether through continuous physical events like a scroll wheel or discrete physical events like the keyboard shortcuts, a user could indicate his intent to "zoom in" and the web application author would only need to listen for a single type of event: zoomrequest
.
IndieUI Events defines a way for web authors to register for these request events on a leaf node element or on a ancestor element acting as an event delegate. Authors also declaritively define which actions or behaviors a view responds to, and when it is appropriate for browsers to initiate these events.
This section is non-normative.
The primary goals of the Events Module specification are declared as the following:
The IndieUI Working Group also hopes to provide a clear path for web developers to smoothly transition from currently existing physical events to IndieUI events, during the period when implementations of IndieUI are incomplete. This will likely be achieved through polyfill implementations in common JavaScript libraries, though such implementations are not a required deliverable of the IndieUI Working Group, and documentation of this development path is not a requirement of this Events Module specification.
This section is non-normative.
Decisions regarding which specific physical user interactions (keyboard combinations, gestures, speech, etc.) initiate IndieUI events are explicitly listed as out-of-scope in the Working Group charter. User interface interaction patterns should be designed and defined by each operating system, rather than defined as part of any technical specification.
However, this document lists informative examples of certain keyboard and mouse events that may initiate each IndieUI event. They are listed here purely to aid in clarifying the reader's conceptual understanding of each event, as well as illustrating certain UI differences between platforms. These informative examples are primarily limited to keyboard and mouse events, because those physical modalities have been common in software interaction for decades, and their use is familiar to most readers.
For example, it may be common for the ESC key to trigger a "dismissrequest" event to close a dialog on most systems, but the specification does not require the user agent to use any particular physical event. It is an implementation detail, and left for the developers of each platform or assistive technology to determine whether ESC or some other interaction is the most appropriate way to initiate the "dismissrequest" event. As long as there is a documented way for end users to initiate each event, the user agent will be considered a conforming implementation.
The following example uses a "dismissrequest" event to close or cancel out of a modal application dialog.
<!-- Declare which IndieUI event(s) this element receives. --> <dialog uiactions="dismiss" id="myDialog"> <!-- Include an optional click trigger for the dismiss action. --> <button uitrigger="dismiss"> Cancel </button> ... other dialog contents ... </dialog> <script type="text/javascript"> var myDialog = document.getElementById("myDialog"); // At some point during runtime, the handler will be called. // For example, if the user presses ESC key while focus is inside the dialog. function dismissHandler(e) { // Cancel and close the dialog. Don't forget to move focus before closing. closeDialog(e.receiver); // Event.receiver is a readonly property like Event.target // Then cancel the event. e.stopPropagation(); // Stop the event from bubbling. e.preventDefault(); // Let the UA/AT know the event was intercepted successfully. } // Register the event at initialization. // Either on the receiver itself, or on an ancestor element like the body. myDialog.addEventListener("dismissrequest", dismissHandler); </script>
This section is non-normative.
One of the core principles behind UI Change Request Events is that they operate on a backwards-compatible, opt-in basis. In other words, the web application author has to first be aware of these events, then explicitly declare each event receiver and register an event listener, or user agents behave as normal and do not initiate these events.
Change request events do not cause any direct manipulation or mutation of the DOM, and do not have any "default action" in the context of a web view. Instead, the event conveys the user's intent to the web application, and allows the web application to perform the appropriate action on behalf of the user, including making potential changes to the DOM. If a web application is authored to understand the change request event, it can cancel the event, which informs the user agent that the event has been captured and understood. If a web application does not cancel the event, the user agent may attempt fallback behavior or communicate to the user that the input has not been recognized.
User interface actions are declared as enumerated token attribute values on an element. Each value corresponds to a specific UI Request Event, and declares the web page author's ability to receive and handle each of the request events initiated by the user agent. In order to receive each request event, authors MUST also register for the event using Element.addEventListener()
at this element or on an ancestor of the element with the corresponding action. User agents SHOULD NOT initiate a UI Request Event when the user's point-of-regard is not inside an element with the corresponding defined action.
uiactions
IDL AttributeThe uiactions
attribute of each instance of the Element
interface MUST return a DOMTokenList reflecting the uiactions
content attribute.
partial interface Element {
readonly attribute DOMTokenList uiactions;
};
uiactions
of type DOMTokenList, readonly uiactions
content attribute. This attribute is readonly b/c DOMTokenList values are modified by methods (e.g. el.uiactions.add("dismiss");
) rather than by string assignment (e.g. NOT el.uiactions = "dismiss";
). Need to make sure this is clear for authors.
uiactions
Content AttributeEvery element may have a uiactions
attribute specified, which is necessary to define the receiver of each type of request event. The attribute, if specified, must have a value that is a set of whitespace-separated tokens representing the various actions to which the web application responds on behalf of this element. The actions that an element has assigned to it consists of all the tokens returned when the value of the uiactions
attribute is split on whitespace. (Duplicates are ignored.)
User agents MUST reflect the uiactions
content attribute in the uiactions
IDL attribute.
collapse
delete
dismiss
expand
medianext
mediapause
mediaprevious
mediastart
mediastop
<!-- Body element is event listener for all events, but event receiver only for "delete" actions. --> <body uiactions="delete"> <!-- This Dialog is the event receiver for "dismiss" actions initiated on any lower-level event target. --> <dialog uiactions="dismiss"> ... </dialog> </body> <script type="text/javascript"> // Registered all of these on the body as an example of event delegation to help illustrate the difference between event... // ...target (document.activeElement or other point-of-regard), receiver (element with defined actions), and listener (body). document.body.addEventListener("dismissrequest", handleDismiss); document.body.addEventListener("deleterequest", handleDelete); </script>
In the previous example, the "deleterequest" event may be fired any time the user's point-of-regard was inside the document, presumably when the user initiateded their platform's physical event to initiate a deletion, such as pressing the DELETE key. However, the "dismissrequest" would only be fired when the user's point-of-regard was inside the dialog.
A user interface trigger is an element whose default behavior (a.k.a. "click" behavior) is to initiate a discrete UIRequestEvent for the current element or an ancestor element. For example, a "close" or "back" button of a dialog would trigger a "dismiss" request, and a "move to trash" button would trigger a "delete" request.
In order to define a trigger element, authors MUST set the value of the uitrigger attribute as a single token value corresponding to an action defined on the current or an ancestor node. User agents MUST NOT initiate a request Event from a trigger element unless the corresponding action is defined on the current element or an ancester element using the uiactions
content attribute or uiactions
IDL attribute.
Web authors MUST ensure trigger elements have an appropriate accessible label, as rendered text or a text alternative. Web authors MUST ensure trigger elements use an appropriate implicit or explicit role (usually button
).
Web authors SHOULD ensure trigger elements are focusable. Web authors MAY forego making a trigger element focusable if the trigger provides redundant functionality already available through another physical interface. For example, a "delete" trigger may not need to be focusable if pressing the Delete key triggers the same request event.
uitrigger
IDL AttributeThe uitrigger
attribute of each instance of the Element
interface MUST return a DOMString reflecting the uitrigger
content attribute.
partial interface Element {
attribute DOMString uitrigger;
};
uitrigger
of type DOMString, uitrigger
content attribute. Consider making this attribute a single DOMToken rather than DOMString; this should be single action token value with whitespace trimmed.
uitrigger
Content AttributeEvery element may have a uitrigger
attribute. The attribute, if specified, must have a value that is a single token representing the UIRequestEvent which should be triggered when this element is clicked or otherwise activated.
User agents MUST reflect the uitrigger
content attribute in the uitrigger
IDL attribute.
uitrigger
token valuescollapse
delete
dismiss
expand
medianext
mediapause
mediaprevious
mediastart
mediastop
The following example demonstrates a view that can be "dismissed" with a "back" button trigger, the ESC key, or other appropriate physical events using a single event handler.
<section uiactions="dismiss"> <!-- "Back" button acts as the dismiss trigger if clicked or activated. --> <button uitrigger="dismiss"> Back </button> ... other view contents ... </section> <script type="text/javascript"> // A single event handler for a number of user events. function handleDismiss(e) { // Dismiss the current view. dismissView(e.receiver); // Event.receiver is a readonly property like Event.target // Then cancel the event. e.stopPropagation(); // Stop the event from bubbling. e.preventDefault(); // Let the UA/AT know the event was intercepted successfully. } document.body.addEventListener("dismissrequest", handleDismiss); </script>
The single event handler is called when a number of physical events occur:
The following example demonstrates a custom media player that uses the same event handler whether a button was activated, a keyboard media key was pressed, or a headphone remote was clicked.
<body uiactions="mediaprevious mediatoggle medianext"> <button uitrigger="mediaprevious"> Previous Track </button> <button uitrigger="mediatoggle"> Play/Pause </button> <button uitrigger="medianext"> Next Track </button> </body> <script type="text/javascript"> // A single event handler for a number of user events. function handleMedia(e) { // Handle the media events from any physical event source. switch (e.type) { case "mediapreviousrequest": previousTrack(); break; case "mediatogglerequest": togglePlayback(); break; case "medianextrequest": nextTrack(); break; default: return; // Event not handled, so return early to prevent canceling event. } // Then cancel the event. e.stopPropagation(); // Stop the event from bubbling. e.preventDefault(); // Let the UA/AT know the event was intercepted successfully. } document.body.addEventListener("mediapreviousrequest", handleMedia); document.body.addEventListener("mediatogglerequest", handleMedia); document.body.addEventListener("medianextrequest", handleMedia); </script>
The single event handler is called when a number of physical events occur:
There is purposefully no request event for activating the default action. Authors are encouraged to use a standard click
event for default actions. Update: Eitan (Mozilla) requested ACTION-53: activate request event to augment default action trigger when click handlers are inappropriate, giving the iOS "slide to unlock" feature as an example.
Event fires on point-of-regard (document.activeElement
, clicked element, or AT equivalent) if applicable, or otherwise document.body
.
Event order: These events should be asynchronous, but when used in conjunction with keyboard events, we need to define where each IndieUI event fires in the chain of keydown
, keyup
, and keypress
. Discrete request events trigger by the keyboard should probably follow "keydown" but precede "keyup". We may also need an identifier to associate each event with other related physical events: e.g. This dismissrequest
event is associated with the keyboard events keydown
, keyup
, and keypress
of the ESC key.
Separate interface from UIEvent because the @uiactions attribute will 1) affect when and where these events are fired, and 2) adds the distinction of an event receiver element (element declaring the uiactions attribute) in addition to the "target" element where the event initiates and the "listener" element where the event is registered for and handled.
The receiver
attribute of each instance of the UIRequestEvent interface MUST return an EventTarget matching the DOMElement where the corresponding event's action is declared via the uiactions
attribute.
[Constructor(DOMString typeArg, optional UIRequestEventInit dictUIRequestEventInit)]
interface UIRequestEvent : UIEvent {
readonly attribute EventTarget receiver;
};
receiver
of type EventTarget, readonly uiactions
content attribute on an ancestor node of the event target. The event receiver is determined using the following steps:
"dismissrequest"
), determine the corresponding uiactions token ("dismiss"
).el.uiactions.contains("dismiss")
). If the current element's action list contains the corresponding uiactions token, the event target is also the event receiver.May also need a way to associate IndieUI events with their physical event counterparts.
[Example] partial interface UIEvent { readonly attribute EventID id; // UID of current event readonly attribute EventList relatedEvents; // List of related events, with ID and potentially type of each event. // This "dismissrequest" event is associated with the previous "keydown" and "keyup" events. }
dictionary UIRequestEventInit : UIEventInit {
boolean bubbles = true;
boolean cancelable = true;
WindowProxy? view = null;
long detail = 0;
EventTarget receiver = null;
};
UIRequestEventInit
Membersbubbles
of type boolean, defaulting to true
cancelable
of type boolean, defaulting to true
detail
of type long, defaulting to 0
receiver
of type EventTarget, defaulting to null
view
of type WindowProxy, nullable, defaulting to null
The different types of UIRequestEvents that can occur are:
expandrequest
)Indicates the user desires to to reveal information in a collapsed section (e.g. a disclosure widget) or branch node in a hierarchy (e.g., a tree view).
collapserequest
)Indicates the user desires to hide or collapse information in an expanded section (e.g. a disclosure widget) or branch node in a hierarchy (e.g., a tree view).
dismissrequest
)Indicates the user desires "dismiss" the current view (e.g. canceling a dialog, or closing a popup menu).
Users, wanting to "escape from" or "dismiss" a web application state (for example, closing a modal dialog), can indicate their intent a number of ways, most commonly by pressing Escape on keyboard-controlled operating systems. Web authors who have registered for this event should process the event to determine whether to cancel the event. If the "dismiss" action is understood in the context of the web application, web authors should perform the appropriate action (such as closing the dialog), and cancel the event using the event object's preventDefault()
method.
deleterequest
)Indicates the user wants to initiate a "delete" action on the current view.
mediapauserequest
)Initiated when the user agent sends a request to the web application to pause media playback.
mediastartrequest
)Initiated when the user agent sends a request to the web application to start media playback.
mediastoprequest
)Initiated when the user agent sends a request to the web application to stop media playback.
mediaplaypauserequest
)Initiated when the user agent sends a request to the web application to toggle the play/pause state of media playback.
mediapreviousrequest
)Initiated when the user agent sends a request to the web application to skip media to the previous track boundary.
medianextrequest
)Initiated when the user agent sends a request to the web application to skip media to the next track boundary.
There may be a need for continuous media request events to support fast-forward or rewind via press-and-hold, or double-click-and-hold, etc.
This section is non-normative.
Authors wishing to conditionally assign request event handlers based on whether the user agent supports these events can use standard objection detection for each event handler or property.
The following example conditionally assigns a "dismissrequest" event based on whether the user agent has support for the feature.
if (typeof document.body.ondismissrequest !== "undefined") { // Okay to use "dismissrequest" event. document.body.addEventListener("dismissrequest", dismissHandler); } else { // Otherwise catch the ESC key or another platform-specific equivalent event. document.body.addEventListener("keyup", keyHandler); }
TBD: define as click or relevant keypress like Enter/Return, and spacebar in some scenarios.
TBD
UIRequestEvents initiate from the element representing the point-of-regard, and are referenced in the Event object's target
property.
For mainstream user agents, the point-of-regard will likely be the element under a pointer (e.g. mouse cursor) or, in the case of keyboard events, the currently focused element (e.g. document.activeElement). Assistive technologies or other alternative inputs may determine another element to be the subject of the user's attention, and initiate events from this new point-of-regard.
Still looking for a better term than point-of-regard.
Attributes whose DOM property and content attribute values remain in sync. Identical to reflected attributes in HTML.
The closest DOM ancestor to the user's point-of-regard that declares the current event type via the uiactions
content attribute or uiactions
DOM attribute. The Request Event Receiver is referenced in the Event object's receiver
property.
UI Request Events are not initiated by the user agent unless the following are true:
@uiactions
element.addEventListener()
Some of this definition will need to be massaged and move into the normative sections above defining @uiactions.
At the time of publishing, the following individuals were active, participating members of the IndieUI Working Group.
The following individual(s) were previously active members of the working group or otherwise significant contributors.
Tab Atkins, Jesse Bunch, Chris Fleizach, Lachlan Hunt, Bradley Meck, Sangwhan Moon, Ryosuke Niwa, Rich Simpson.
This publication has been funded in part with Federal funds from the U.S. Department of Education, National Institute on Disability, Independent Living, and Rehabilitation Research (NIDILRR) under contract number ED-OSE-10-C-0067. The content of this publication does not necessarily reflect the views or policies of the U.S. Department of Education, nor does mention of trade names, commercial products, or organizations imply endorsement by the U.S. Government.