Element: <oj-menu>

Oracle® JavaScript Extension Toolkit (JET)
4.2.0

E91398-01

QuickNav

Attributes

PREVIEW: This is a preview API. Preview APIs are production quality, but can be changed on a major version without a deprecation path.

Version:
  • 4.2.0
Since:
  • 0.6

JET Custom Elements

JET components are implemented as custom HTML elements. A detailed description of working with these elements can be found in: JET Custom Element Usage.

Description: Themeable, WAI-ARIA-compliant popup menu with touch, mouse and keyboard interactions for navigation.

A JET Menu is created using an ( <oj-menu> ) tag with an ( <oj-option> ) tag representing each menu item:

<oj-menu id="menu" style="display:none" aria-label="Order Edit">
  <oj-option>Item 1</oj-option>
  <oj-option>Item 2</oj-option>
  <oj-option>Item 3</oj-option>
  <oj-option>Item 4</oj-option>
  <oj-option>Item 5</oj-option>
</oj-menu>

JET Menus are not intended to be scrollable, as large, unwieldy menus are not good UX. Ideally menus should have a manageable number of items.

JET Menu is a popup component, for use with context menu, menu button, or similar functionality. It is not intended to sit inline on the page. See also the JET NavigationList component.

For this reason, the component is automatically hidden until it is opened. However, this styling is not applied until the component is initialized. To avoid a FOUC (flash of unstyled content), applications are encouraged to apply style="display:none" to the menu markup, as shown in the above code sample.

Dividers

Divider elements can be created by including menu items that contain only spaces and/or dashes, or nothing at all:

<oj-menu id="menu" style="display:none" aria-label="Order Edit">
  <oj-option>Item 1</oj-option>
  <oj-option>---</oj-option>
  <oj-option>Item 2</oj-option>
</oj-menu>

For WAI-ARIA compliance, JET automatically adds role="separator" to the divider element.

Menu Item Icons

Menu items currently support the rendering of start and end icons. To render start or end icons for a menu item, the startIcon or endIcon slot of the oj-option should be specified. See the oj-option doc for details about accepted children and slots.

Dismissal

JET Menus auto-dismiss in the expected cases, such as focus loss and menu item selection. In addition, Sheet Menus offer the following optional dismissal affordances:

  • A "Cancel" menu item is displayed for Sheet Menus if the $menuSheetCancelAffordance SASS variable is set to "menuItem". See its translation and subId.
  • The user can dismiss Sheet Menus via a downward swipe on the menu if the $menuSheetSwipeDownBehavior SASS variable is set to "dismiss".

Touch End User Information

Target Gesture Action
Menu Item Tap Invoke the menu item's action.
Menu Swipe Down Dismiss the menu, if "swipe to dismiss" is enabled by the application.
JET Component or HTML Element having a JET Context Menu Press & Hold Open the context menu.
Outside of Menu Touch Close the menu.

Disabled items do not allow any touch interaction.

Keyboard End User Information

Target Key Action
Menu Item Enter or Space Invoke the focused menu item's action.
UpArrow Move focus to the previous menu item, wrapping around at the top.
DownArrow Move focus to the next menu item, wrapping around at the bottom.
Home Move focus to the first menu item.
End Move focus to the last menu item.
Menu Item in Top-level Menu Esc Close the menu and move focus to the launcher.
JET Component or HTML Element having a JET Context Menu Shift + F10 Open the context menu.

* RTL refers to pages written in a right-to-left language such as Arabic.

Typing a letter moves focus to the first item whose title starts with that character. Repeating the same character cycles through matching items. Typing more characters within the one second timer matches those characters.

Note that the "Search for text when I start typing" feature in Firefox can interfere with web content that accepts keystrokes, such as this "type a letter" feature of JET Menu.

Disabled items can receive keyboard focus, but do not allow any other interaction.

Accessibility

The app should supply either an aria-label or aria-labelledby attribute on the menu's root element, except possibly for menu buttons as discussed below.

If a menu is shared by different launchers, and should have a different label for each launcher, then a ojBeforeOpen listener can be used to set a different label per launch.

For a menu launched exclusively by one or more menu buttons, these attributes are optional. When the menu is opened via the menu button UI, if neither attribute is present after all ojBeforeOpen listeners have been called, then aria-labelledby will be set on the menu, referencing the menu button, and will be removed when the menu is closed. This approach provides a useful default label, while allowing the app to supply a different label if desired, and while allowing the menu to be shared by several menu buttons and/or other launchers.

Disabled content: JET supports an accessible luminosity contrast ratio, as specified in WCAG 2.0 - Section 1.4.3 "Contrast", in the themes that are accessible. (See the "Theming" chapter of the JET Developer Guide for more information on which themes are accessible.) Note that Section 1.4.3 says that text or images of text that are part of an inactive user interface component have no contrast requirement. Because disabled content may not meet the minimum contrast ratio required of enabled content, it cannot be used to convey meaningful information.

Reparenting

When a menu is opened, it will be reparented in the document and reparented back when closed. The goal of this design is to maintain as much of the page author's document structure as possible, while avoiding most of the clipping and positioning issues of a completely inline design.

If opened from another popup, the menu will be reparented to the nearest parent popup. Otherwise, the menu will be reparented to a container in the document body.

The context of opening is defined by the resolved openOptions.launcher value, which can be set via the attribute, via the argument to the open() method, or via a ojBeforeOpen listener.

All menus are assigned the same z-index values. The layering between peer popups reflects the opening order. In addition, the page author has control over z-index weights by way of the menu's layer. The menu's layer defines the "stacking context" and assignd the "oj-menu-layer" style.

Some notable consequences of this design:

  • Events raised within the menu will not bubble up to the menu's original ancestors. Instead, listeners for menu events should be applied to either the menu's root element, or the document.
  • Likewise, developers should not use CSS descendant selectors, or similar logic, that assumes that the menu will remain a child of its original parent.

Performance

If a menu launcher (such as a menu button or item with a context menu) is stamped inside a table, dataGrid, or other container, the resulting set of launchers should share a single menu defined outside the container.

Reading direction

The only supported way to set the reading direction (LTR or RTL) is to set the "dir" attribute on the <html> element of the page. As with any JET component, in the unusual case that the reading direction is changed post-init, the menu must be refresh()ed, or the page must be reloaded.

Declarative Binding

For components like Menu and Buttonset that contain a number of like items, applications may wish to use a foreach Knockout binding to stamp out the contents. This binding cannot live on the same node as the JET ojComponent binding, and must instead live on a nested virtual element as follows:

<oj-menu id="menu" style="display:none" aria-label="Order Edit">
    <!-- ko foreach: menuItems -->
        <oj-option data-bind="attr: {id: id, disabled: disabled}">
            <span data-bind="text: label"></span>
        </oj-option>
    <!-- /ko -->
</oj-menu>

Note: Application logic should not interact with the component's properties or invoke its methods until the BusyContext indicates that the component is ready for interaction.

Slots

JET elements can have up to two types of child content:

  • Any child element with a slot attribute will be moved into that named slot, e.g. <span slot='startIcon'>...</span>. All supported named slots are documented below. Child elements with unsupported named slots will be removed from the DOM.
  • Any child element lacking a slot attribute will be moved to the default slot, also known as a regular child.

contextMenu

The contextMenu slot is set on the oj-menu within this element. This is used to designate the JET Menu that this component should launch as a context menu on right-click, Shift-F10, Press & Hold, or component-specific gesture. If specified, the browser's native context menu will be replaced by the JET Menu specified in this slot.

The application can register a listener for the Menu's ojBeforeOpen event. The listener can cancel the launch via event.preventDefault(), or it can customize the menu contents by editing the menu DOM directly, and then calling refresh() on the Menu.

To help determine whether it's appropriate to cancel the launch or customize the menu, the ojBeforeOpen listener can use component API's to determine which table cell, chart item, etc., is the target of the context menu. See the JSDoc and demos of the individual components for details.

Keep in mind that any such logic must work whether the context menu was launched via right-click, Shift-F10, Press & Hold, or component-specific touch gesture.

Example

Initialize the component with a context menu:

<oj-some-element>
    <-- use the contextMenu slot to designate this as the context menu for this component -->
    <oj-menu slot="contextMenu" style="display:none" aria-label="Some element's context menu">
...
    </oj-menu>
</oj-some-element>

Attributes

disabled :boolean

Disables the menu if set to true.
Default Value:
  • false
Names
Item Name
Property disabled
Property change event disabledChanged
Property change listener attribute (must be of type function) on-disabled-changed
Examples

Initialize the menu with the disabled attribute specified:

<oj-menu disabled='true'></oj-menu>

Get or set the disabled property after initialization:

// getter
var disabledValue = myMenu.disabled;

// setter
myMenu.disabled = true;

open-options :Object

A collection of settings impacting the launch of a menu. These openOptions may be accessed and overridden individually or collectively, as seen in the examples.

The values set here can be overridden on a per-launch basis by passing the corresponding params into the open method. Those per-launch values can be further customized by a ojBeforeOpen listener.

The built-in menu button and context menu functionality overrides some of the Menu's openOptions, for WAI-ARIA compliance and other reasons. Thus, if the app really wants to customize those values, it must do so in a ojBeforeOpen listener. If the built-in menu button or context menu functionality is modified in this way, it is the app's responsibility to ensure that the result is both correct and accessible.

Names
Item Name
Property openOptions
Property change event openOptionsChanged
Property change listener attribute (must be of type function) on-open-options-changed
Examples

Initialize the menu, setting some openOptions values.

<oj-menu open-options.initial-focus='true' open-options.launcher='myLauncher'></oj-menu>

Get or set the openOptions attribute, after initialization:

// Get one
var value = myMenu.openOptions.launcher;

// Get all
var values = myMenu.openOptions;

// Set one, leaving the others intact
myMenu.openOptions.initialFocus = 'none';

// Set many.  Any existing openOptions not listed are lost
myMenu.openOptions = { 'launcher': 'myLauncher',
                       'initialFocus': 'firstItem',
                       'position': myPositionObj };

open-options.display :string

Determines whether the menu is displayed as a dropDown menu or a sheet menu.

The default value is "auto", in which case the behavior is a function of the screen width and the $menuDropDownThresholdWidth SASS variable. For example, if that variable is set to 768px, then for screen widths of 768px and larger, the menu will display as a dropDown, and for screen widths less than 768px, the menu will display as a sheet.

To avoid disorienting the user, if the screen width changes while the menu is already open (e.g. due to a device rotation), the display may not change until the next launch.

If the SASS variable is set to 0 or a huge value such as 99999px, then all menus with display set to "auto" will always display as a dropDown or sheet, respectively.

Supported Values:
Name Type Description
"auto" string Displays the menu as a sheet or dropDown, depending on the screen width.
"dropDown" string Displays the menu as a dropDown.
"sheet" string Displays the menu as a sheet.
Default Value:
  • "auto"
Since:
  • 2.1.0
Names
Item Name
Property openOptions.display
Examples

Initialize the menu with the openOptions.display sub-option specified:

<oj-menu open-options.display='dropDown'></oj-menu>

Get or set the openOptions.display sub-option, after initialization:

// getter
var display = myMenu.openOptions.display;

// setter:
myMenu.openOptions.display = 'sheet';

open-options.initial-focus :string

Determines focus behavior when the menu is initially opened.
Supported Values:
Name Type Description
"firstItem" string Focuses the first menu item (e.g. MenuButton DownArrow behavior).
"menu" string Focuses the menu itself, with no menu item focused (e.g. typical Context Menu behavior).
"none" string Leaves focus where it is, e.g. on the launching component. The application must verify that the result is accessible.
Default Value:
  • "menu"
Names
Item Name
Property openOptions.initialFocus
Examples

Initialize the menu with the openOptions.initialFocus sub-option specified:

<oj-menu open-options.initial-focus='firstItem'></oj-menu>

Get or set the openOptions.initialFocus sub-option, after initialization:

// getter
var initialFocus = myMenu.openOptions.initialFocus;

// setter:
myMenu.openOptions.initialFocus = 'none';

open-options.launcher :string|Object

The DOM node (which may or may not be a JET component) that launches this menu. This node must be focusable, as focus is returned to it upon menu dismissal.

The launcher must either be specified in this component option, or on each menu launch -- see open() and ojBeforeOpen.

Default Value:
  • null
Names
Item Name
Property openOptions.launcher
Examples

Initialize the menu with the openOptions.launcher sub-option specified:

<oj-menu open-options.launcher='myLauncher'></oj-menu>

Get or set the openOptions.launcher sub-option, after initialization:

// getter
var launcher = myMenu.openOptions.launcher;

// setter:
myMenu.openOptions.launcher = 'myLauncher';

open-options.position :Object

Determines the position of a dropDown menu when launched via the open() method or via menu button or context menu functionality. Ignored for sheet menus.

Please refer to the jQuery UI Position utility for details about the various choices. In addition to that syntax, note that JET supports the following reading direction-aware extended syntax in the my and at fields:

  • JET supports start and end values wherever left and right are supported. The start value means "left in LTR; right in RTL", while the end value means "right in LTR; left in RTL."
  • Similarly, JET supports > and < operators wherever + and - are supported. The > value means "+ in LTR; - in RTL", while the < value means "- in LTR; + in RTL." E.g. a my value of "start>40" shifts the menu 40px "endward," while a my value of "start<40" shifts the menu 40px "startward."

Menu also supports the following extended syntax for the of field:

  • The "event" keyword means "position the menu relative to the UI event that opened the menu."
  • The "launcher" keyword means "position the menu relative to the launcher element."

By default, when the of field is not set, the menu is positioned relative to the launcher.

Default Value:
  • { "my": "start top", "at": "start bottom", "collision": "flipfit" }
Names
Item Name
Property openOptions.position
Examples

Initialize the menu with the openOptions.position option specified:

<oj-menu open-options.position.my='start'></oj-menu>

Get or set the openOptions.position sub-option, after initialization:

// Get one field of position object
var position = myMenu.openOptions.position.my;

// Get entire position object
var position = myMenu.openOptions.position;

// Set one field of position object, leaving the others intact
myMenu.openOptions.position.at = 'end bottom';

// Set entire position object. Any fields not listed are lost.
myMenu.openOptions.position = { 'my': 'start top', 'at': 'end<5 top+5', 'collision': 'flipfit' };< code>

translations :Object

A collection of translated resources from the translation bundle, or null if this component has no resources. Resources may be accessed and overridden individually or collectively, as seen in the examples.

If this component has translations, their documentation immediately follows this doc entry.

Default Value:
  • an object containing all resources relevant to the component, or null if none
Names
Item Name
Property translations
Property change event translationsChanged
Property change listener attribute (must be of type function) on-translations-changed
Examples

Initialize the component, overriding some translated resources and leaving the others intact:

<!-- Using dot notation -->
<oj-some-element translations.some-key='some value' translations.some-other-key='some other value'></oj-some-element>

<!-- Using JSON notation -->
<oj-some-element translations='{"someKey":"some value", "someOtherKey":"some other value"}'></oj-some-element>

Get or set the translations property after initialization:

// Get one
var value = myComponent.translations.someKey;

// Set one, leaving the others intact. Always use the setProperty API for 
// subproperties rather than setting a subproperty directly.
myComponent.setProperty('translations.someKey', 'some value');

// Get all
var values = myComponent.translations;

// Set all.  Must list every resource key, as those not listed are lost.
myComponent.translations = {
    someKey: 'some value',
    someOtherKey: 'some other value'
};

translations.label-cancel :string

Label for the "Cancel" menu item.

See the translations attribute for usage examples.

Default Value:
  • "Cancel"
Since:
  • 2.1.0
Names
Item Name
Property translations.labelCancel

Events

ojAction

Triggered when a menu item (other than the built-in "Cancel" item) is selected.

To ensure keyboard accessibility, the only correct, supported way to react to the selection of a menu item is to listen for this event. Click listeners and href navigation should not be used.

Properties:

All of the event payloads listed below can be found under event.detail.

Name Type Description
event Event a custom event
Properties
Name Type Description
detail Object an object containing component specific event info
Examples

Specify an ojAction listener via the DOM attribute:

<oj-menu on-oj-action='[[listener]]'></oj-menu>

Specify an ojAction listener via the JavaScript property:

myMenu.onOjAction = listener;

Add an ojAction listener via the addEventListener API:

myMenu.addEventListener('ojAction', listener);

ojAnimateEnd

Triggered when a default animation has ended, such as when the component is being opened/closed or a child item is being added/removed. This event is not triggered if the application has called preventDefault on the animateStart event.
Properties:

All of the event payloads listed below can be found under event.detail.

Name Type Description
event Event a custom event
Properties
Name Type Description
detail Object an object containing component specific event info
Properties
Name Type Description
element Element target of animation
action string The action that is starting the animation. The number of actions can vary from component to component. Suggested values are:
  • "open" - when a menu component is opened
  • "close" - when a menu component is closed
Examples

Bind an event listener to the onOjAnimateEnd property to listen for the "open" ending animation:

myMenu.onOjAnimateEnd = function( event )
  {
    // verify that the component firing the event is a component of interest and action
     is open
    if (event.detail.action == "open") {}
  };

Bind an event listener to the onOjAnimateEnd property to listen for the "close" ending animation:

myMenu.onOjAnimateEnd = function( event )
  {
    // verify that the component firing the event is a component of interest and action
     is close
    if (event.detail.action == "close") {}
  };

Specify an ojAnimateEnd listener via the DOM attribute:

<oj-menu on-oj-animate-end='[[listener]]'></oj-menu>

Specify an ojAnimateEnd listener via the JavaScript property:

myMenu.onOjAnimateEnd = listener;

Add an ojAnimateEnd listener via the addEventListener API:

myMenu.addEventListener('ojAnimateEnd', listener);

ojAnimateStart

Triggered when a default animation is about to start, such as when the component is being opened/closed or a child item is being added/removed. The default animation can be cancelled by calling event.preventDefault. The default animations are controlled via the theme (SCSS) :

// dropdown menu
$menuDropDownOpenAnimation: (effect: "zoomIn", transformOrigin: "#myPosition", duration: $animationDurationShort) !default;
$menuDropDownCloseAnimation: (effect: "none") !default;

// sheet menu
$menuSheetOpenAnimation: (effect: "slideIn", direction: "top", duration: $animationDurationShort) !default;
$menuSheetCloseAnimation: (effect: "slideOut", direction: "bottom", duration: $animationDurationShort) !default;

Properties:

All of the event payloads listed below can be found under event.detail.

Name Type Description
event CustomEvent a custom event
Properties
Name Type Description
detail Object an object containing component specific event info
Properties
Name Type Description
action string The action that is starting the animation. The number of actions can vary from component to component. Suggested values are:
  • "open" - when a menu is opened
  • "close" - when a menu is closed
element Element target of animation
endCallback function If the event listener calls event.preventDefault to cancel the default animation, it must call the endCallback function when it finishes its own animation handling and any custom animation has ended.
Examples

Bind an event listener to the onOjAnimateStart property to override the default "open" animation:

myMenu.onOjAnimateStart = function( event )
  {
    // verify that the component firing the event is a component of interest and action
     is open
    if (event.detail.action == "open") {
      event.preventDefault();
      oj.AnimationUtils.fadeIn(event.detail.element).then(event.detail.endCallback);
  };
  

Bind an event listener to the onOjAnimateStart property to override the default "close" animation:

myMenu.onOjAnimateStart = function( event )
  {
    // verify that the component firing the event is a component of interest and action
     is close
    if (event.detail.action == "close") {
      event.preventDefault();
      oj.AnimationUtils.fadeOut(event.detail.element).then(event.detail.endCallback);
  };

Specify an ojAnimateStart listener via the DOM attribute:

<oj-menu on-oj-animate-start='[[listener]]'></oj-menu>

Specify an ojAnimateStart listener via the JavaScript property:

myMenu.onOjAnimateStart = listener;

Add an ojAnimateStart listener via the addEventListener API:

myMenu.addEventListener('ojAnimateStart', listener);

ojBeforeOpen

Triggered before this menu is launched via the open method or via menu button or context menu functionality. The launch can be cancelled by calling event.preventDefault().

The ui.openOptions payload field contains the settings being used for this menu launch, resulting from merging the openOptions passed to open(), if any, with the openOptions component option.

This field is "live", meaning that the listener can alter fields such as position to affect this launch without affecting the component option. Since these changes are applied to the merged object, they supersede both the openOptions passed to open() and the openOptions component option.

If any of the above techniques are used to alter the built-in menu button or context menu functionality, it is the app's responsibility to ensure that the result is both correct and accessible.

Properties:

All of the event payloads listed below can be found under event.detail.

Name Type Description
event Event a custom event
Properties
Name Type Description
detail Object an object containing component specific event info
Properties
Name Type Description
openOptions Object Settings in use for this menu launch.
Examples

Specify an ojBeforeOpen listener via the DOM attribute:

<oj-menu on-oj-before-open='[[listener]]'></oj-menu>

Specify an ojBeforeOpen listener via the JavaScript property:

myMenu.onOjBeforeOpen = listener;

Add an ojBeforeOpen listener via the addEventListener API:

myMenu.addEventListener('ojBeforeOpen', listener);

ojClose

Triggered after this menu is closed.

Properties:

All of the event payloads listed below can be found under event.detail.

Name Type Description
event Event a custom event
Since:
  • 2.0.0
Examples

Specify an ojClose listener via the DOM attribute:

<oj-menu on-oj-close='[[listener]]'></oj-menu>

Specify an ojClose listener via the JavaScript property:

myMenu.onOjClose = listener;

Add an ojClose listener via the addEventListener API:

myMenu.addEventListener('ojClose', listener);

ojOpen

Triggered after this menu is launched via the open method or via menu button or context menu functionality.

Properties:

All of the event payloads listed below can be found under event.detail.

Name Type Description
event Event a custom event
Since:
  • 2.0.0
Examples

Specify an ojOpen listener via the DOM attribute:

<oj-menu on-oj-open='[[listener]]'></oj-menu>

Specify an ojOpen listener via the JavaScript property:

myMenu.onOjOpen = listener;

Add an ojOpen listener via the addEventListener API:

myMenu.addEventListener('ojOpen', listener);

Methods

getProperty(property) → {*}

Retrieves a value for a property or a single subproperty for complex properties.
Parameters:
Name Type Description
property string The property name to get. Supports dot notation for subproperty access.
Returns:
Type
*
Example

Get a single subproperty of a complex property:

var subpropValue = myComponent.getProperty('complexProperty.subProperty1.subProperty2');

open(event, openOptions)

Launches this menu after firing the ojBeforeOpen event. Listeners to that event can cancel the launch via event.preventDefault(). If the launch is not canceled, then the the open event is fired after the launch.

This method's optional openOptionsparam can be used to specify per-launch values for the settings in the corresponding component options, without altering those options. Those per-launch values can be further customized by a ojBeforeOpen listener.

Menus launched manually (as opposed to those launched by built-in functionality such as the menu button and context menu functionality) must be launched via this API, not by simply unhiding the Menu DOM (such as via jQuery's show() API.

Parameters:
Name Type Argument Description
event Object <optional>
What triggered the menu launch. May be null. May be omitted if subsequent params are omitted.
openOptions Object <optional>
Options to merge with the openOptions option. May be null. May be omitted if subsequent params are omitted.
Example

Invoke the open method:

// override the launcher for this launch only, without affecting the other
// openOptions, and without affecting the component's openOptions option
myMenu.open(myEvent, {'launcher': 'myLauncher'});

refresh()

Refreshes the visual state of the menu. JET components require a refresh() after the DOM is programmatically changed underneath the component. For Menu, this includes:
  • After menu items are added or removed.
  • After a change to a menu item's disabled status.
  • After the reading direction (LTR vs. RTL) changes.
Example

Invoke the refresh method:

myMenu.refresh();

setProperties(properties)

Performs a batch set of properties.
Parameters:
Name Type Description
properties Object An object containing the property and value pairs to set.
Example

Set a batch of properties:

myComponent.setProperties({"prop1": "value1", "prop2.subprop": "value2", "prop3": "value3"});

setProperty(property, value)

Sets a property or a single subproperty for complex properties and notifies the component of the change, triggering a [property]Changed event.
Parameters:
Name Type Description
property string The property name to set. Supports dot notation for subproperty access.
value * The new value to set the property to.
Example

Set a single subproperty of a complex property:

myComponent.setProperty('complexProperty.subProperty1.subProperty2', "someValue");