Commit 4f5eff80 authored by Jono Mingard's avatar Jono Mingard
Browse files

Update dropdown nav keyboard controls for Bootstrap theme (Bug #1482455)



Dropdown menus now show when they have the 'open' class as well as
on hover, and the dropdown navigation script has been updated to use
the new menu bar classes (and with better code style)

behatnotneeded: doesn't seem to be any good way to fire specific
keyboard events at the page in order to test this

Change-Id: I1bf17183c0510914f16ada7299cfddd6ce29e907
Signed-off-by: default avatarJono Mingard <jonom@catalyst.net.nz>
parent d98a5eac
......@@ -8,54 +8,55 @@
* @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');
jQuery(document).ready(function($) {
var navigationkeys = {
LEFT: 37, UP: 38, RIGHT: 39, DOWN: 40, SPACE: 32, ENTER: 13, ESC: 27, TAB: 9
};
var menuhoverclass = 'show-menu';
var menulinks = menu.find('> li > span > a');
var menuid = 'dropdown-nav';
var menu = $('ul#' + menuid);
// Add aria attributes to menus
$j(menulinks).each(function() {
var submenus = $j(this).parent().next('ul');
var menuhoverclass = 'open';
var menulinks = menu.find('> li > a');
if (submenus.length > 0) {
submenus.attr({ 'aria-hidden': 'true' });
}
// Add aria attributes to menus
menulinks.each(function() {
$(this).siblings('ul.has-dropdown').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);
// Open menus on focus
menulinks.focus(function() {
$(this).closest('ul').find('.'+menuhoverclass)
.attr('aria-hidden', 'true').removeClass(menuhoverclass);
$(this).siblings('ul.has-dropdown')
.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
}
// Stop two dropdowns from opening at once when focusing one menu item and hovering another
menulinks.mouseenter(close_open_menu);
$j(menulinks).keydown(function(e) {
// Key bindings to navigate top-level menu items
menulinks.keydown(function(e) {
if (e.keyCode == navigationkeys.LEFT) {
// Previous menu
if ($j(this).closest('li').prev('li').length > 0) {
if ($(this).closest('li').prev('li').length > 0) {
e.preventDefault();
$j(this).closest('li').prev('li').find('a').first().focus();
$(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) {
if ($(this).closest('li').next('li').length > 0) {
e.preventDefault();
$j(this).closest('li').next('li').find('a').first().focus();
$(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');
var prevmenu = $(this).closest('li').prev('li').find('ul');
if (prevmenu.length > 0) {
e.preventDefault();
$j(this).closest('li').find('.'+menuhoverclass)
$(this).closest('li').find('.'+menuhoverclass)
.attr('aria-hidden', 'true').removeClass(menuhoverclass);
prevmenu.attr('aria-hidden', 'false').addClass(menuhoverclass)
.find('a').last().focus();
......@@ -63,44 +64,45 @@ function setup_navigation() {
}
else if (e.keyCode == navigationkeys.DOWN) {
// Open menu, select first item
if ($j(this).closest('li').find('ul').length > 0) {
if ($(this).closest('li').find('ul').length > 0) {
e.preventDefault();
$j(this).closest('li').find('ul')
$(this).closest('li').find('ul')
.attr('aria-hidden', 'false').addClass(menuhoverclass)
.find('a').first().focus();
}
}
else if (e.keyCode == navigationkeys.SPACE) {
else if (e.keyCode == navigationkeys.SPACE || e.keyCode == navigationkeys.ENTER) {
// If submenu is hidden, open it
e.preventDefault();
$j(this).closest('li').find('ul[aria-hidden=true]')
$(this).closest('li').find('ul')
.attr('aria-hidden', 'false').addClass(menuhoverclass)
.find('a').first().focus();
}
else if (e.keyCode == navigationkeys.ESC) {
// Close all submenus
e.preventDefault();
$j('.'+menuhoverclass)
$('.'+menuhoverclass)
.attr('aria-hidden', 'true').removeClass(menuhoverclass);
}
});
var links = $j(menulinks).closest('li').find('ul').find('a');
$j(links).keydown(function(e) {
// Key bindings to navigate dropdown submenus
var links = menulinks.closest('li').find('ul').find('a');
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();
if ($(this).closest('li').prev('li').length === 0) {
$(this).parents('ul').parents('li').find('a').first().focus();
}
else {
$j(this).closest('li').prev('li').find('a').first().focus();
$(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 ($(this).closest('li').next('li').length === 0) {
var nextmenuitem = $(this).closest('ul').parent('li').next('li');
if (nextmenuitem.length > 0) {
e.preventDefault();
nextmenuitem.find('a').first().focus();
......@@ -108,13 +110,13 @@ function setup_navigation() {
}
else {
e.preventDefault();
$j(this).closest('li').next('li').find('a').first().focus();
$(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');
var prevmenuitem = $(this).closest('ul').parent('li').prev('li');
if (prevmenuitem.length > 0) {
prevmenuitem.find('a').first().focus();
}
......@@ -122,7 +124,7 @@ function setup_navigation() {
else if (e.keyCode == navigationkeys.RIGHT) {
// Focus next main menu item
e.preventDefault();
var nextmenuitem = $j(this).closest('ul').parent('li').next('li');
var nextmenuitem = $(this).closest('ul').parent('li').next('li');
if (nextmenuitem.length > 0) {
nextmenuitem.find('a').first().focus();
}
......@@ -130,31 +132,29 @@ function setup_navigation() {
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);
var ul = $(this).closest('ul');
ul.siblings('a').focus();
ul.attr('aria-hidden', 'true').removeClass(menuhoverclass);
}
else if (e.keyCode == navigationkeys.SPACE) {
e.preventDefault();
window.location = $j(this).attr('href');
window.location = $(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);
close_open_menu();
}
});
$j(document).click(function() {
$j('.'+menuhoverclass).attr('aria-hidden', 'true').removeClass(menuhoverclass);
});
menu.click(function(e) {
e.stopPropagation();
});
}
$(document).click(close_open_menu);
jQuery(document).ready(function() {
setup_navigation();
// Helper to close any open dropdowns
function close_open_menu() {
$('#' + menuid + ' .' + menuhoverclass).attr('aria-hidden', 'true').removeClass(menuhoverclass);
}
});
This diff is collapsed.
......@@ -117,9 +117,10 @@
}
// on hover, if dropdown. show
// show dropdown on hover or with 'open' class
@media (min-width: $screen-sm-min) {
ul.nav > li:hover > ul.has-dropdown.child-nav {
ul.nav > li:hover > ul.has-dropdown.child-nav,
ul.nav > li > ul.has-dropdown.child-nav.open {
display: block;
visibility: visible;
}
......
This diff is collapsed.
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