Commit 584de240 authored by Jono Mingard's avatar Jono Mingard
Browse files

Make dropdown navigation keyboard-accessible (Bug #1259397)



Changed the dropdown navigation to be navigable using the keyboard (if
Javascript is enabled).

- The tab key navigates all menus and submenus in order
- Links in the main navigation bar can be navigated with the left and right
  arrows
- Submenus can be navigated with the up and down arrows and can be closed with
  the Esc key. Pressing left or right opens the previous or next menu

Note: Dropdown navigation works as before with JS disabled and only requires
it for keyboard accessibility.

Change-Id: Ia7aeb33526c71a2fe3ea4c635f4a8aa17c64e21b
Signed-off-by: default avatarJono Mingard <jonom@catalyst.net.nz>
parent d9248922
/**
* Keyboard accessibility for dropdown navigation
* @source: http://gitorious.org/mahara/mahara
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later
* @copyright For copyright information on Mahara, please see the README file distributed with this software.
*
*/
// Sets up keyboard controls for dropdown navigation
function setup_navigation() {
var menu = $j('.main-nav > ul');
var menuhoverclass = 'show-menu';
var menulinks = menu.find('> li > span > a');
// Add aria attributes to menus
$j(menulinks).each(function() {
var submenus = $j(this).parent().next('ul');
if (submenus.length > 0) {
submenus.attr({ 'aria-hidden': 'true' });
}
});
$j(menulinks).focus(function(){
$j(this).closest('ul')
.find('.'+menuhoverclass).attr('aria-hidden', 'true').removeClass(menuhoverclass);
$j(this).parent().next('ul').attr('aria-hidden', 'false').addClass(menuhoverclass);
});
// Bind navigation keys
var navigationkeys = {
LEFT: 37, UP: 38, RIGHT: 39, DOWN: 40, SPACE: 32, ESC: 27, TAB: 9
}
$j(menulinks).keydown(function(e) {
if (e.keyCode == navigationkeys.LEFT) {
// Previous menu
if ($j(this).closest('li').prev('li').length > 0) {
e.preventDefault();
$j(this).closest('li').prev('li').find('a').first().focus();
}
}
else if (e.keyCode == navigationkeys.RIGHT) {
// Next menu
if ($j(this).closest('li').next('li').length > 0) {
e.preventDefault();
$j(this).closest('li').next('li').find('a').first().focus();
}
}
else if (e.keyCode == navigationkeys.UP) {
// Select last item in previous menu
var prevmenu = $j(this).closest('li').prev('li').find('ul');
if (prevmenu.length > 0) {
e.preventDefault();
$j(this).closest('li').find('.'+menuhoverclass)
.attr('aria-hidden', 'true').removeClass(menuhoverclass);
prevmenu.attr('aria-hidden', 'false').addClass(menuhoverclass)
.find('a').last().focus();
}
}
else if (e.keyCode == navigationkeys.DOWN) {
// Open menu, select first item
if ($j(this).closest('li').find('ul').length > 0) {
e.preventDefault();
$j(this).closest('li').find('ul')
.attr('aria-hidden', 'false').addClass(menuhoverclass)
.find('a').first().focus();
}
}
else if (e.keyCode == navigationkeys.SPACE) {
// If submenu is hidden, open it
e.preventDefault();
$j(this).closest('li').find('ul[aria-hidden=true]')
.attr('aria-hidden', 'false').addClass(menuhoverclass)
.find('a').first().focus();
}
else if (e.keyCode == navigationkeys.ESC) {
// Close all submenus
e.preventDefault();
$j('.'+menuhoverclass)
.attr('aria-hidden', 'true').removeClass(menuhoverclass);
}
});
var links = $j(menulinks).closest('li').find('ul').find('a');
$j(links).keydown(function(e) {
if (e.keyCode == navigationkeys.UP) {
// Focus previous submenu item or parent menu item
e.preventDefault();
if ($j(this).closest('li').prev('li').length == 0) {
$j(this).parents('ul').parents('li').find('a').first().focus();
}
else {
$j(this).closest('li').prev('li').find('a').first().focus();
}
}
else if (e.keyCode == navigationkeys.DOWN) {
// Focus next submenu item or next main menu item
if ($j(this).closest('li').next('li').length == 0) {
var nextmenuitem = $j(this).closest('ul').parent('li').next('li');
if (nextmenuitem.length > 0) {
e.preventDefault();
nextmenuitem.find('a').first().focus();
}
}
else {
e.preventDefault();
$j(this).closest('li').next('li').find('a').first().focus();
}
}
else if (e.keyCode == navigationkeys.LEFT) {
// Focus previous main menu item
e.preventDefault();
var prevmenuitem = $j(this).closest('ul').parent('li').prev('li');
if (prevmenuitem.length > 0) {
prevmenuitem.find('a').first().focus();
}
}
else if (e.keyCode == navigationkeys.RIGHT) {
// Focus next main menu item
e.preventDefault();
var nextmenuitem = $j(this).closest('ul').parent('li').next('li');
if (nextmenuitem.length > 0) {
nextmenuitem.find('a').first().focus();
}
}
else if (e.keyCode == navigationkeys.ESC) {
// Exit submenu
e.preventDefault();
$j(this)
.closest('ul').first().prev('span').find('a').focus()
.closest('ul').first().find('.'+menuhoverclass).attr('aria-hidden', 'true').removeClass(menuhoverclass);
}
else if (e.keyCode == navigationkeys.SPACE) {
e.preventDefault();
window.location = $j(this).attr('href');
}
});
// Hide menu if click or focus occurs outside of navigation
menu.find('a').last().keydown(function(e) {
if (e.keyCode == navigationkeys.TAB) {
$j('.'+menuhoverclass).attr('aria-hidden', 'true').removeClass(menuhoverclass);
}
});
$j(document).click(function() {
$j('.'+menuhoverclass).attr('aria-hidden', 'true').removeClass(menuhoverclass);
});
menu.click(function(e) {
e.stopPropagation();
});
}
jQuery(document).ready(function() {
setup_navigation();
});
......@@ -554,11 +554,12 @@ $string['processing'] = 'Processing';
$string['unknownerror'] = 'An unknown error occurred (0x20f91a0)';
// menu
$string['overview'] = 'Overview';
$string['home'] = 'Home';
$string['Content'] = 'Content';
$string['myportfolio'] = 'Portfolio';
$string['settings'] = 'Settings';
$string['dropdownmenu'] = 'menu';
$string['overview'] = 'Overview';
$string['home'] = 'Home';
$string['Content'] = 'Content';
$string['myportfolio'] = 'Portfolio';
$string['settings'] = 'Settings';
$string['myfriends'] = 'My friends';
$string['findfriends'] = 'Find friends';
$string['groups'] = 'Groups';
......
......@@ -429,6 +429,7 @@ EOF;
if ($dropdownmenu) {
$smarty->assign('DROPDOWNMENU', $dropdownmenu);
$javascript_array[] = $jsroot . 'dropdown-nav.js';
}
$smarty->assign('MOBILE', $SESSION->get('mobile'));
......
......@@ -300,7 +300,7 @@ div.pagination a {
color: #FFFFFF;
font-weight: normal;
}
.main-nav li a:hover {
.main-nav li a:hover, .main-nav li a:focus {
color: #1F97D5;
text-decoration: underline;
}
......@@ -349,7 +349,7 @@ div.pagination a {
background: #00084F;
color: #FFFFFF;
}
.main-nav .dropdown-sub li a:hover {
.main-nav .dropdown-sub li a:hover, .main-nav .dropdown-sub li a:focus {
background: #1F97D5;
}
/******************** Tabs ********************/
......
......@@ -158,7 +158,7 @@ div.pagination a {
background: none;
color: #FFFFFF;
}
.main-nav li a:hover {
.main-nav li a:hover, .main-nav li a:focus {
background: none;
}
.main-nav li.selected a,
......@@ -202,7 +202,7 @@ div.pagination a {
color: #FFFFFF;
background: #F6871F;
}
.main-nav .dropdown-sub li a:hover {
.main-nav .dropdown-sub li a:hover, .main-nav .dropdown-sub li a:focus {
color: #1A428F;
background: #FFF4EA;
}
......
......@@ -269,7 +269,7 @@ div.pagination a {
border-right: 1px solid #4C711D;
border-left: 1px solid #AFBB87;
}
.main-nav li a:hover {
.main-nav li a:hover, .main-nav li a:focus {
color: #e4ffb4;
}
.main-nav li.selected a,
......@@ -333,7 +333,7 @@ div.pagination a {
border-left: 0;
border-right: 0;
}
.main-nav .dropdown-sub li a:hover {
.main-nav .dropdown-sub li a:hover, .main-nav .dropdown-sub li a:focus {
color: #4C711D;
background: #d5d5d5;
border-top: 1px solid #e8e8e8;
......
......@@ -5,7 +5,7 @@
<ul id="{if $DROPDOWNMENU}dropdown-nav{else}nav{/if}">
{strip}
{foreach from=$MAINNAV item=item}
<li class="{if $item.path}{$item.path}{else}dashboard{/if}{if $item.selected} selected{/if}{if $DROPDOWNMENU} dropdown-nav-home{/if}"><span><a href="{$WWWROOT}{$item.url}"{if $item.accesskey} accesskey="{$item.accesskey}"{/if} class="{if $item.path}{$item.path}{else}dashboard{/if}">{$item.title}</a></span>
<li class="{if $item.path}{$item.path}{else}dashboard{/if}{if $item.selected} selected{/if}{if $DROPDOWNMENU} dropdown-nav-home{/if}"><span><a href="{$WWWROOT}{$item.url}"{if $item.accesskey} accesskey="{$item.accesskey}"{/if} class="{if $item.path}{$item.path}{else}dashboard{/if}">{$item.title}{if $DROPDOWNMENU && $item.submenu} <span class="accessible-hidden">({str tag=dropdownmenu})</span>{/if}</a></span>
{if $item.submenu}
<ul class="{if $DROPDOWNMENU}dropdown-sub {/if}rd-subnav">
{strip}
......
......@@ -315,7 +315,7 @@ div.warning,
color: #FFFFFF;
font-weight: normal;
}
.main-nav li a:hover {
.main-nav li a:hover, .main-nav li a:focus {
color: #FFFFFF;
}
.main-nav li.selected a,
......@@ -361,7 +361,7 @@ div.warning,
background: #1b3b46;
color: #FFFFFF;
}
.main-nav .dropdown-sub li a:hover {
.main-nav .dropdown-sub li a:hover, .main-nav .dropdown-sub li a:focus {
background: #00aef1;
color: #FFFFFF;
}
......
......@@ -325,7 +325,7 @@ div.pagination a {
line-height: 40px;
background-color: transparent;
}
.main-nav li a:hover {
.main-nav li a:hover, .main-nav li a:focus {
color: #f6871f;
background-color: transparent;
}
......@@ -504,7 +504,7 @@ div.pagination a {
border-top: 1px solid #0f1978;
border-bottom: 1px solid #000533;
}
.main-nav .dropdown-sub li a:hover {
.main-nav .dropdown-sub li a:hover, .main-nav .dropdown-sub li a:focus {
background: #0b1960;
color: #ffd64d;
}
......
......@@ -1465,7 +1465,7 @@ div.rel {
text-decoration: none;
font-weight: bold;
}
.main-nav li a:hover {
.main-nav li a:hover, .main-nav li a:focus {
background: #E9E9E9;
}
.ie7 .main-nav li {
......@@ -1517,7 +1517,7 @@ div.rel {
.ie7 .main-nav .dropdown-sub {
top: 36px;
}
.main-nav li:hover .dropdown-sub {
.main-nav li:hover .dropdown-sub, .main-nav li .dropdown-sub.show-menu {
display: block;
}
.main-nav .dropdown-sub li {
......@@ -1542,7 +1542,7 @@ div.rel {
.main-nav .dropdown-sub li.selected a {
font-weight: bold;
}
.main-nav .dropdown-sub li a:hover {
.main-nav .dropdown-sub li a:hover, .main-nav .dropdown-sub li a:focus {
background: #DDDDDD;
}
/******************** Tabs ********************/
......
......@@ -57,7 +57,7 @@ a:focus {
.main-nav li a:link,
.main-nav li a:visited,
.main-nav li a:active,
.main-nav li a:hover {
.main-nav li a:hover, .main-nav li a:focus {
color: {$data.navfg};
}
.main-nav li.selected a,
......@@ -85,7 +85,7 @@ a:focus {
color: {$data.navfg} !important;
background: {$data.navbg} !important;
}
.main-nav .dropdown-sub li a:hover {
.main-nav .dropdown-sub li a:hover, .main-nav .dropdown-sub li a:focus {
color: {$data.subfg} !important;
background: {$data.subbg} !important;
}
......
......@@ -5,7 +5,7 @@
<ul id="{if $DROPDOWNMENU}dropdown-nav{else}nav{/if}">
{strip}
{foreach from=$MAINNAV item=item}
<li class="{if $item.path}{$item.path}{else}dashboard{/if}{if $item.selected} selected{/if}{if $DROPDOWNMENU} dropdown-nav-home{/if}"><span><a href="{$WWWROOT}{$item.url}"{if $item.accesskey} accesskey="{$item.accesskey}"{/if} class="{if $item.path}{$item.path}{else}dashboard{/if}">{$item.title}</a></span>
<li class="{if $item.path}{$item.path}{else}dashboard{/if}{if $item.selected} selected{/if}{if $DROPDOWNMENU} dropdown-nav-home{/if}"><span><a href="{$WWWROOT}{$item.url}"{if $item.accesskey} accesskey="{$item.accesskey}"{/if} class="{if $item.path}{$item.path}{else}dashboard{/if}">{$item.title}{if $DROPDOWNMENU && $item.submenu} <span class="accessible-hidden">({str tag=dropdownmenu})</span>{/if}</a></span>
{if $DROPDOWNMENU}{if $item.submenu}
<ul class="dropdown-sub">
{strip}
......
......@@ -295,7 +295,7 @@ div.pagination a {
.main-nav li a:active {
color: #FFFFFF;
}
.main-nav li a:hover {
.main-nav li a:hover, .main-nav li a:focus {
color: #FFFFFF;
background: none;
}
......@@ -347,7 +347,7 @@ div.pagination a {
color: #8E1901;
background: #e1cfa1;
}
.main-nav .dropdown-sub li a:hover {
.main-nav .dropdown-sub li a:hover, .main-nav .dropdown-sub li a:focus {
color: #FFFFFF;
background: #c66c14;
}
......
......@@ -297,7 +297,7 @@ ul.groupuserstatus li .submit input.submit:active {
color: #FFFFFF;
font-weight: normal;
}
.main-nav li a:hover {
.main-nav li a:hover, .main-nav li a:focus {
color: #FFFFFF;
}
.main-nav li.selected a,
......@@ -340,7 +340,7 @@ ul.groupuserstatus li .submit input.submit:active {
background: #F3D2F4;
font-weight: normal;
}
.main-nav .dropdown-sub li a:hover {
.main-nav .dropdown-sub li a:hover, .main-nav .dropdown-sub li a:focus {
color: #FFFFFF;
background: #ab57af;
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment