Keyboard navigation in the map seems to require use of a Mouse

1740
4
Jump to solution
11-09-2012 08:05 AM
JoanneMcGraw
Occasional Contributor III
Hello all,

My particular interest in the keyboard navigation functionality is related to providing WCAG 2.0 Accessibility requirements (where all functionality must be operable through a keyboard) in my applications.

I've noticed that on the  http://sampleserver6.arcgisonline.com/arcgis/rest/services/SampleWorldCities/MapServer?f=jsapi page, handling of the '+', '-' and arrow keys is only effected when the mouse hovers over the map object itself. As long as the mouse cursor is over the map, those keys properly trigger their respective events; but, if you move your mouse cursor (not click, just move it) up into the title section, for example, those key events are no longer trapped.

So, as keyboard accessibility requires that I not depend on the use of a mouse for functionality, I am wondering if there is some way to more fully provide keyboard navigation to the map object. That is, I cannot find a way to tab to the map and have it activate and respond to the keyboard navigation listeners.

Can anyone suggest how this might be accomplished? While I can't assume a mouse, I can't assume it will not exist either and it appears as though a mouseout event turns off all listeners to the map. Can I get around this somehow?

Cheers,
jtm
0 Kudos
1 Solution

Accepted Solutions
JoanneMcGraw
Occasional Contributor III
Thank you for your response, geos_rfleet.

Well then, to close this thread off...

By default, isZoomSlider is set to true in the map object. We never override this in our maps, so the zoom slider always appears within our maps. We're using that element as a visual clue that the map "has focus" (in reality, the zoom slider has focus, but that's a detail an end user isn't typically going to be aware of). What's important is that the zoom slider element can be tabbed to with the keyboard.

So, we have attached key event listeners to the zoom slider so that when it has focus, certain key presses will result in particular keyboard navigation actions on the map whether the mouse is over it or not.

It should be noted in doing this (and this is certainly referenced in geos_rfleet's response), that you have to stop propagation of the event or else, if your mouse IS over the map, it will perform the action twice or, if the user is pressing an arrow key, the browser page will also pan accordingly.

We use ExtJs as our GUI library, so I'm not sure how useful it will be to include the code here, but just in case it is of use to someone...

var keyMap = new Ext.util.KeyMap(     {         target: "<mapDiv>_zoom_slider"         ,binding: [             {                 key: [187,61] // Shift-'+'                 ,shift: true                 ,ctrl: false                 ,alt: false                 ,scope: this.map                 ,fn: function(key, evt){                     evt.stopEvent();                     this.setLevel(this.getLevel() + 1);                 }             }             ,{                 key: [107,43] // Numeric keypad '+'                 ,shift: false                 ,ctrl: false                 ,alt: false                 ,scope: this.map                 ,fn: function(key, evt){                     evt.stopEvent();                     this.setLevel(this.getLevel() + 1);                 }             }             ,{                 key: [189,173,109,45] // '-' (incl. Numeric keypad                 ,shift: false                 ,ctrl: false                 ,alt: false                 ,scope: this.map                 ,fn: function(key, evt){                     evt.stopEvent();                     this.setLevel(this.getLevel() - 1);                 }             }             ,{                 key: [98, 40] // down arrow, incl. Numeric keypad                 ,shift: false                 ,ctrl: false                 ,alt: false                 ,scope: this.map                 ,fn: function(key, evt){                     evt.stopEvent();                     this._fixedPan(0, this.height * 0.0135);                 }             }             ,{                 key: [100, 37] // left arrow, incl. Numeric keypad                 ,shift: false                 ,ctrl: false                 ,alt: false                 ,scope: this.map                 ,fn: function(key, evt){                     evt.stopEvent();                     this._fixedPan(this.width * -0.0135, 0);                 }             }             ,{                 key: [102, 39] // right arrow, incl. Numeric keypad                 ,shift: false                 ,ctrl: false                 ,alt: false                 ,scope: this.map                 ,fn: function(key, evt){                     evt.stopEvent();                     this._fixedPan(this.width * 0.0135, 0);                 }             }             ,{                 key: [104, 38] // up arrow, incl. Numeric keypad                 ,shift: false                 ,ctrl: false                 ,alt: false                 ,scope: this.map                 ,fn: function(key, evt){                     evt.stopEvent();                     this._fixedPan(0, this.height * -0.0135)                 }             }         ]     } );


The following page was a good resource for this work also: http://unixpapa.com/js/key.html

Cheers,
jtm

View solution in original post

0 Kudos
4 Replies
JoanneMcGraw
Occasional Contributor III
Does anyone have any comment on this? That is, that there doesn't seem to be a way to set focus on the map to support keyboard events without the use of a mouse? Is there just something that needs to be enabled properly somewhere?

cheers,
jtm
0 Kudos
__Rich_
Occasional Contributor III
You are correct in that the map class connects the key* event handlers when onmouseenter fires and disconnects them when onmouseleave fires.

I've only had a brief glance through the code but I don't think there's an obvious (non-hacky) way for you to manually keep the map connected to the relevant events.

Can't think of an immediate reason why there's anything stopping you listening for the key events (on an element of your choosing) then calling the appropriate methods on the map.

You will just have to be careful not to introduce a counter-intuitive user experience e.g. disabling the built-in keyboard navigation might be necessary to avoid double hops, especially if your user does have a mouse...
0 Kudos
JoanneMcGraw
Occasional Contributor III
Thank you for your response, geos_rfleet.

Well then, to close this thread off...

By default, isZoomSlider is set to true in the map object. We never override this in our maps, so the zoom slider always appears within our maps. We're using that element as a visual clue that the map "has focus" (in reality, the zoom slider has focus, but that's a detail an end user isn't typically going to be aware of). What's important is that the zoom slider element can be tabbed to with the keyboard.

So, we have attached key event listeners to the zoom slider so that when it has focus, certain key presses will result in particular keyboard navigation actions on the map whether the mouse is over it or not.

It should be noted in doing this (and this is certainly referenced in geos_rfleet's response), that you have to stop propagation of the event or else, if your mouse IS over the map, it will perform the action twice or, if the user is pressing an arrow key, the browser page will also pan accordingly.

We use ExtJs as our GUI library, so I'm not sure how useful it will be to include the code here, but just in case it is of use to someone...

var keyMap = new Ext.util.KeyMap(     {         target: "<mapDiv>_zoom_slider"         ,binding: [             {                 key: [187,61] // Shift-'+'                 ,shift: true                 ,ctrl: false                 ,alt: false                 ,scope: this.map                 ,fn: function(key, evt){                     evt.stopEvent();                     this.setLevel(this.getLevel() + 1);                 }             }             ,{                 key: [107,43] // Numeric keypad '+'                 ,shift: false                 ,ctrl: false                 ,alt: false                 ,scope: this.map                 ,fn: function(key, evt){                     evt.stopEvent();                     this.setLevel(this.getLevel() + 1);                 }             }             ,{                 key: [189,173,109,45] // '-' (incl. Numeric keypad                 ,shift: false                 ,ctrl: false                 ,alt: false                 ,scope: this.map                 ,fn: function(key, evt){                     evt.stopEvent();                     this.setLevel(this.getLevel() - 1);                 }             }             ,{                 key: [98, 40] // down arrow, incl. Numeric keypad                 ,shift: false                 ,ctrl: false                 ,alt: false                 ,scope: this.map                 ,fn: function(key, evt){                     evt.stopEvent();                     this._fixedPan(0, this.height * 0.0135);                 }             }             ,{                 key: [100, 37] // left arrow, incl. Numeric keypad                 ,shift: false                 ,ctrl: false                 ,alt: false                 ,scope: this.map                 ,fn: function(key, evt){                     evt.stopEvent();                     this._fixedPan(this.width * -0.0135, 0);                 }             }             ,{                 key: [102, 39] // right arrow, incl. Numeric keypad                 ,shift: false                 ,ctrl: false                 ,alt: false                 ,scope: this.map                 ,fn: function(key, evt){                     evt.stopEvent();                     this._fixedPan(this.width * 0.0135, 0);                 }             }             ,{                 key: [104, 38] // up arrow, incl. Numeric keypad                 ,shift: false                 ,ctrl: false                 ,alt: false                 ,scope: this.map                 ,fn: function(key, evt){                     evt.stopEvent();                     this._fixedPan(0, this.height * -0.0135)                 }             }         ]     } );


The following page was a good resource for this work also: http://unixpapa.com/js/key.html

Cheers,
jtm
0 Kudos
__Rich_
Occasional Contributor III
So you've employed exactly the approach I suggested...but presumably 'pre' and not 'post' my response 😉

In the spirit of WCAG, I would think a more obvious/explicit way for the user to 'turn on' keyboard navigation would be appropriate, otherwise it just feels a bit like lip service, IMHO.
0 Kudos