Accessibility

If you haven't already, please read about ARIA and WCAG accessibility guidelines, as introduced at Accessibility Information (MDN).

By default each GoJS Diagram provides the ability for users who cannot or do not want to use a mouse but who can use a keyboard to use most tools that diagrams provide for manipulation. The focus navigation and virtual pointer capabilities must be enabled in each diagram, either programmatically or interactively.

By default each GoJS Diagram provides generic support for screen readers when using focus navigation. However, because the GoJS library cannot know what is important to say and when, it is important for each diagram to customize exactly what is said for each state that the diagram might be in.

Focus Navigation and Virtual Pointer

With version 3.1 the CommandHandler supports a "keyboard control" mode in which the user can use the keyboard to interact with a diagram without using the mouse. Nearly any kind of diagram can be controlled this way, except for commands or tools that are very time-dependent or that require specific pointing precision such as freehand drawing or cursive writing.

This keyboard control mechanism supports a way to use the keyboard instead of the mouse to focus on a node or a link or on a button or editable TextBlock or port within a node or a link, and to act on it or to change focus to a nearby or related node or link or button or port. This kind of GoJS focus depends on but is in addition to the regular HTML DOM focus mechanism, because GraphObjects are not HTML elements and cannot by themselves receive DOM keyboard events.

This mechanism only works as long as the diagram has HTML keyboard focus. If another HTML element gains HTML focus, including when the user Tabs out of the diagram, the commands described in this page no longer apply.

The GoJS focus navigation mechanism is independent of the diagram's selection mechanism, although it does make it easy to select or deselect a focused node or link. Focus is shown using a single Adornment that is the CommandHandler.focusBox. It is added to the GraphObject that is the CommandHandler.focus, the GraphObject that has GoJS focus.

The keyboard control mechanism also supports a virtual pointer to allow keyboard events to produce mouse InputEvents for invoking and controlling tools. The virtual pointer is shown as a single Part, the CommandHandler.virtualPointerBox in the "Tool" Layer, when a Shift key is held down. Shift-arrow or numpad keys move it around, and other Shift modified keys perform actions to generate mouse InputEvents or modify future generated mouse InputEvents.

Enable the GoJS keyboard control functionality in any Diagram by Ctrl-Alt-Enter. That command actually toggles whether or not the focus navigation and virtual pointer system are enabled. When CommandHandler.isFocusEnabled and CommandHandler.isVirtualPointerEnabled are false (the default) there is no difference in behavior from earlier version libraries, except for the addition of this Ctrl-Alt-Enter command to toggle those two properties on and off.

Keyboard Command Table

For all of these commands, you can freely interchange Meta (the Command key on macOS) with Ctrl.

Activating a GraphObject (including a Part) means calling its click event handler, or editing a TextBlock if it is TextBlock.editable, or showing its toolTip. Use the ContextMenu key to show a GraphObject.contextMenu.

Keys Focus and Virtual Pointer Effect
Ctrl-Alt-Enter or Ctrl-Alt-NumpadEnter toggle keyboard control mode
ArrowUp and ArrowDown and ArrowLeft and ArrowRight change focus to nearby object within scope (top-level Parts, Group members, or GraphObjects within a Part)
Ctrl-ArrowUp and Ctrl-ArrowDown and Ctrl-ArrowLeft and Ctrl-ArrowRight change focus from Node to a connected Link or to the next or the previous connected Link or Node
Enter or NumpadEnter focus enters Group subgraph or Part's GraphObjects or activates focused GraphObject within a Part
Escape focus leaves for containing Part or containing Group
Ctrl-Enter or Ctrl-NumpadEnter activates focused GraphObject, even if the focus is a Part
Space select focused Part or show tooltip for focused GraphObject
Ctrl-Space toggle focused Part selection or show tooltip for focused GraphObject
Shift-Space add focused Part to selection or show tooltip for focused GraphObject
ShiftLeft or ShiftRight show virtual pointer when a Shift key is down and not when the key is up
Shift-ArrowUp and Shift-ArrowDown and Shift-ArrowLeft and Shift-ArrowRight move virtual pointer 10 units in given direction
Ctrl-Shift-ArrowUp and Ctrl-Shift-ArrowDown and Ctrl-Shift-ArrowLeft and Ctrl-Shift-ArrowRight move virtual pointer 1 unit in given direction
Shift-Numpad1 through Shift-Numpad9, except for Shift-Numpad5 move virtual pointer 10 units in given direction; 2, 4, 6, 8 down, left, right, up; 1, 3, 7, and 9 on the diagonal
Ctrl-Shift-Numpad1 through Ctrl-Shift-Numpad9, except for Ctrl-Shift-Numpad5 move virtual pointer 1 unit in given direction; 2, 4, 6, 8 down, left, right, up; 1, 3, 7, and 9 on the diagonal
Shift-Enter or Shift-NumpadEnter or Shift-Numpad5 mouse button down or up
Shift-A toggle alt modifier for next mouse button event
Shift-C toggle control modifier for next mouse button event
Shift-D or Shift-NumpadDecimal toggles the click count between 1 and 2 for the next mouse button up event, which auto-resets to 1
Shift-M toggle meta modifier for next mouse button event
Shift-S toggle shift modifier for next mouse button event
Shift-1 use left button modifier on next mouse button event
Shift-2 use middle button modifier on next mouse button event
Shift-3 use right button modifier on next mouse button event
Shift-Numpad0 toggles between the left and right button modifiers for the next mouse button event

All other commands operate in the standard manner, as listed in Standard Keyboard Commands. They mostly operate on the current Diagram.selection; none of them operate on the current CommandHandler.focus.

Focus Navigation

The focus navigation mode handles arrow keys and the Enter, Escape, Space, and ContextMenu keys to allow the user to change which GraphObject has focus and to perform operations on what has focus. The focus is completely independent of the Diagram.selection and Diagram.highlighteds collections and mechanisms. All other keyboard commands should operate normally. For example, the Delete command will always operate on the selection, not on the focus.

As focus changes, either a built-in element or a programmer supplied one can provide a screen reader some descriptive text to read aloud. Read more about this at Customizing for Screen Readers.

A Simulation

Here is a simulation, using the Robot extension, of using Arrow keys and Enter, Space, and Escape as if the user is controlling the focus in a diagram. As the Robot simulates keyboard commands, they are recorded in a list that you can see.

The first keyboard command is Ctrl-Alt-Enter in order to enable CommandHandler.isFocusEnabled. Focus is given to a root Node, in this case the "Alpha" Node.

Ctrl-arrow keys follow connected Links from a focused Node, so the first Ctrl-ArrowDown key changes focus from the "Alpha" Node to the Link going to the "Beta" Node. A Ctrl-ArrowRight would change focus to the second Link coming out of the "Alpha" Node. The next command, Ctrl-ArrowDown, changes focus to that "Beta" Node.

Then the Enter key changes focus to be on editable TextBlocks, buttons, and ports of the Node. Now the arrow keys focus on the next GraphObject within the Node in the direction determined by the arrow key. The ArrowRight key focuses on the "TreeExpanderButton", which is then clicked twice with the Enter key.

The Escape key leaves the context inside the Node back to the Node itself. Then the ArrowRight key chooses the first Part to the right of the current focus, which is the "Gamma" Node. It is selected with the Space key, and deleted by the Delete key. (Remember that CommandHandler commands operate on the selection, not on the focus.)

Then it undoes the deletion, and programmatically focuses on the "Epsilon" Node. A couple arrow key commands changes focus to the "Delta" Node and then the "Beta" Node. The ContextMenu key brings up the context menu.

There are two "ContextMenuButtons", with focus on the first one, which is invoked by the Enter key. The context menu command puts out a string to the Robot Log. Focus is automatically returned to the Node, and finally an ArrowUp key changes focus back to the "Alpha" node.

There are no Groups in this example, but if there had been, once a Group gets focus, the Enter key acts just as it does on any Node -- it changes focus to a GraphObject in the Group that is a button or port or editable TextBlock. Hitting Escape, if the Group has any member Parts, would change focus to one of those member Nodes. Once a member Part has focus, the Escape key changes focus back up to the Group itself.

Try it

You can try using focus navigation in any of our samples, or in your own app. For example you can try it in the Incremental Tree sample. Click in the diagram and type Ctrl-Alt-Enter to enable focus changing behavior.

When a Node (or a Link) has focus you need to type Enter to change focus to any buttons (clickable GraphObjects) or editable TextBlocks or ports or GraphObjects with context menus or tool tips that are within the Node. When there is more than one, use the arrow keys to get to the GraphObject that you want. In the case of the Incremental Tree sample, the only eligible GraphObject within the Node is a "TreeExpanderButton". When it has focus you can type Enter to click on the button, expanding or collapsing its subtree.

When you are navigating amongst the GraphObjects of a Node or a Link, the arrow keys stay within the Part. They only change focus to other eligible GraphObjects within that Part, if there are any. Remember to type Escape to change focus back to the containing Node, so that you can use the arrow keys to navigate to a different Node.

If you are navigating in a diagram with Groups, you need to Enter a Group in order to focus on any of its member Parts. The arrow keys are limited to navigating amongst the member Parts of that one Group. You need to Escape to leave the subgraph and focus on the Group as a whole again, so that you can navigate to other Nodes in the diagram.

Virtual Pointer

When the user holds down a Shift key, it goes into virtual pointer mode, where the virtualPointerBox shows where the virtual pointer is, and Shift-arrow keys move that virtualPointerBox around. Moving the virtualPointerBox results in mouse-move InputEvents. Also holding down the Ctrl modifier when typing arrow keys offers finer positioning at one document unit per keystroke. Shift-Enter toggles whether the virtual pointer is 'down' or 'up', also shown in the virtualPointerBox with a thick dark-cyan circle indicating 'down', and results in mouse-down or mouse-up InputEvents.

Basically, to drag a node, type Shift-Enter to do the mouse-down, type Shift-arrow keys to move the virtual pointer around, and finally type Shift-Enter again to do the mouse-up and stop the drag. Note that Shift does not need to be held down the whole time during what would be a mouse-down/moves/up sequence. This allows almost any mouse Tool to run, except ones that involve timing which can be hard to control. Changing the current focus object automatically cancels any Tool running due to the virtual pointer mechanism, which helps with the times that the user forgets to do the second Shift-Enter for the mouse-up effect.

Shift-Space changes the focus to be the Part at the current virtualPointerLocation.

Because Shift and Ctrl are used to control the virtual pointer, there is no natural way to specify modifiers during virtual mouse events, such as control-drag-and-drop in order to copy the selection. Nor is there a reasonable way to specify which button to use for mouse-down or mouse-up events, nor to control whether it's a single click or a double click. So there are additional commands to control those aspects of future simulated mouse input events:

Virtual Pointer Appearance

The current modifiers/button/clickCount state is reflected in the standard virtualPointerBox, as shown in the following simulation controlled by another instance of Robot. The Diagram.contextClick event handler has been set to a function that logs "context clicked!".

A Simulation

This demonstration involves both focus navigation and use of the virtual pointer to draw a new Link and to resize a Node.

First the user types Ctrl-Alt-Enter in order to enable CommandHandler.isVirtualPointerEnabled. The user then uses the arrow keys to navigate to the "Delta" Node. An Enter changes focus to the GraphObjects within the Node. Arrow keys change focus to the bottom left port.

Now the user starts the virtual pointer mode by holding down the Shift key. The virtual pointer starts at the center of the current CommandHandler.focus. An Enter key produces a mouse down event. Note that the virtual pointer has a darker circle, showing that the mouse is down during the mouse move. Then the following Shift-arrow keys start the LinkingTool to draw a new link. Then an Enter key produces a mouse up event, finishing drawing the new link.

The focus has remained on the starting port in the "Delta" Node, so an Escape key changes focus to that Node. An ArrowDown and a Space key cause the "Epsilon" Node to be selected, showing its resize handles.

The virtual pointer starts with a Shift, and right arrow keys move it to the resize handle on the right side. Note that one can use the Ctrl modifier with the Shift-arrow keys in order to move it just one document unit at a time, instead of the default 10 document units.

The ResizingTool is started by a mouse down caused by a Shift-Enter. Following Shift-arrow keys drag that resize handle, and another Shift-Enter produces a mouse up event, finishing the resizing of the node.

General Concepts

The Ctrl and Meta modifier keys are interchangeable for all built-in command keyboard shortcuts. Use whatever works well on your keyboard. But note that not all Tools are that way. For example, the Ctrl modifier when using the DraggingTool is replaced by the Alt modifier on macOS, following the convention there.

The Caps Lock, Insert, and Scroll Lock keys are not used for GoJS keyboard control, and thus can be used by screen readers.

Tab normally changes the HTML element that has DOM keyboard focus, and that behavior is unmodified while the diagram has keyboard focus. If the diagram is showing scrollbars, Tab will focus on the diagram's HTML element holding the scrollbars, allowing for the arrow keys to scroll normally.

Note that nearly all of the regular keyboard-shortcut commands are unaffected by focus navigation. This includes the common commands: delete, cut, copy, paste, selectAll, undo, redo, *Zoom, group, ungroup, editTextBlock. However the arrow keys do not scroll, and the ContextMenu key works on the focus, not on the selection, unless there is no focus object.

Events from an actual mouse input device, perhaps caused by accidentally moving it, may interfere with the intended behaviors using the virtual pointer.

Customizing for Screen Readers

You will want to provide your own function as the CommandHandler.focusChanged event handler in order to customize exactly what is said and shown as the user changes the CommandHandler.focus to a new Node or Link, or to a button or editable TextBlock or port within a Node, or to null.

Your page will probably want to have an element that is updated as your CommandHandler.focusChanged function is called. The CommandHandler needs to know about that element, so you also need to set CommandHandler.liveElementId to refer to that element. It will set the aria-labelledby attribute to refer to your element. Do not set the aria-live attribute on that element.

Exactly what information you put there should be very specific to your particular diagram. Only you the programmer can know exactly what your users will want or need to know as they traverse your diagram, and what language and terminology they will be expecting.

You can call CommandHandler.describe in order to generate the default description for a focused GraphObject, given the previous focus.

In Windows Narrator, when focus gets to the diagram, turn off scan mode (CapsLock-Space) so that the diagram is not considered a simple image, thereby allowing GoJS focus navigation. This action is not needed when using NVDA on Windows or Voice Over on macOS.

Although you can programmatically turn on focus navigation mode in the diagram by setting the CommandHandler.isFocusEnabled and CommandHandler.isVirtualPointerEnabled properties, you could document to your users that they can invoke the Ctrl-Alt-Enter keyboard command in order to enable all of this functionality.

For examples, examine the Accessibility sample, which provides some customizations for the Org Chart Editor sample, or the IVR Tree sample.

If you compare the implementations of the Accessibility and Org Chart Editor samples, you will see that a function named describe has been added to better describe each of the Nodes, Links, and GraphObjects within Nodes.