Description: The column navigation is a navigation menu that renders in columns, whenever you click on a selecttion it either performs an action or expands a new column with children elements.
508 Guidelines: Just tab through the menu as regular buttons or use your arrow keys to navigate trhought the menu.
<div class="nav-list" role="application" title="This is a column navigation. Press enter to start browsing the options. Use your arrow keys to navigate through them and press enter, spacebar or the right arrow to make a selection. To exit navigation press The escape key."></div>
Copy
<div class="nav-list" role="application" title="This is a column navigation. Press enter to start browsing the options. Use your arrow keys to navigate through them and press enter, spacebar or the right arrow to make a selection. To exit navigation press The escape key."></div><script type="text/javascript">
$.makeId = function() {
var
text = "",
possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (var i = 0; i < 5; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
};
$.fn.navlist = function(a) {
var
args = $.extend({
items: [], // an object that contains the items of the nav.
growFunction: function() {}, // gets triggered when the nav expands to a new column
shrinkFunction: function() {}, // gets triggered when the nav shrinks to the previous column,
width: null,
height: null
}, a),
deployEventListener = function(f) {
// we bind this only once to the document to prevent multiple binding of the same event over and over.
// We re-bind only if necessary within the same function
// we also provide a namespace with the id of the list to overwrite the same eventlisterner if fired more than once
// just once--V
$(document).one('keydown.navlist', function(e) {
var el;
switch (e.which) {
case 37: // left
var theUl = f.parent().prev();
removeHigherLevels(theUl);
el = theUl.find('.selected');
clearSelected(el);
break;
case 38: // up
el = f.prev().not('.nav-list-heading');
break;
case 13:
case 32:
case 39: // right
f.trigger('click');
el = f.parent().next().children('li.nav-list-action:first');
break;
case 40: // down
el = f.next();
break;
case 27: //esc
el = f.parent().parent();
el.find('li').removeAttr('tabindex');
break;
default:
return; // exit this handler for other keys
}
if (el.length) {
el.focus();
} else {
deployEventListener(f);
}
e.preventDefault(); // prevent the default action (scroll / move caret)
});
},
removeEventListener = function() {
$(document).off('keydown.navlist');
},
removeHigherLevels = function(myUl) {
// get the index of my ul and remove thos that are after it
var ulIndex = myUl.index();
myUl.parent().children(':gt(' + ulIndex + ')').remove();
if (typeof(args.shrinkFunction) == 'function') args.shrinkFunction();
},
clearSelected = function(b) {
b.parent().find('.selected').removeClass('selected');
},
openList = function(b) {
b.addClass('selected');
var bid = b.attr('id');
var myUl = b.closest('ul');
removeHigherLevels(myUl);
if ($('#navlist-' + bid).length === 0) {
var nuUl = genItemsHtml(b.data('children'), bid);
myUl.after(nuUl);
}
},
createButton = function(ob, ind, heading) {
var buttonLabel = (heading ? '<p class="sr-only">' + heading + '</p>' : '') + ob.label;
var button = $(document.createElement('li')).attr({
'class': 'nav-list-action',
'data-item-id': ind,
'id': $.makeId(),
'tabindex':'-1'
}).html(buttonLabel).focus(function() {
deployEventListener($(this));
}).blur(function() {
removeEventListener();
});
if (typeof(ob.childList) != 'undefined') {
button.addClass('has-children').data('children', ob.childList).click(function() {
clearSelected($(this));
openList($(this));
if (typeof(args.growFunction) == 'function') args.growFunction();
}).html(buttonLabel + '<span class="sr-only"> Press enter, spacebar, or the right arrow key to expand the sub menu.</span>');
} else {
button.click(function() {
var t = $(this);
var ul = t.closest('ul');
clearSelected($(this));
removeHigherLevels(ul);
if ((typeof(ob.action) == 'function') && (ob.action)) {
ob.action();
}
});
}
return button;
},
genItemsHtml = function(itemsObj, parentId) {
var ul = $(document.createElement('ul')).attr('id', 'navlist-' + parentId).addClass('list-block pull-left');
var heading = (((typeof(itemsObj.heading) !== 'undefined') && itemsObj.heading) ? itemsObj.heading : null);
if (args.width) {
ul.width(args.width);
}
if (heading) ul.append('<li aria-hidden="true" class="nav-list-heading">' + itemsObj.heading + '</li>');
for (var i = 0; i < itemsObj.items.length; i++) {
var li = createButton(itemsObj.items[i], i, heading);
ul.append(li);
}
return ul;
};
return this.each(function() {
var t = $(this);
if (args.height) t.height(args.height);
var enterNavEl = function() {
$(document).one('keydown.activateColumnNav', function(e) {
switch (e.which) {
case 39: //right
case 40: // down
t.next().focus();
break;
case 37:
case 38:
t.prev().focus();
break;
case 13:
// make elements navigable
t.find('li.nav-list-action').attr('tabindex', '0');
t.find('.nav-list-action:first').focus();
break;
default:
//re-add event listnener
enterNavEl();
return;
}
e.preventDefault(); // prevent the default action (scroll / move caret)
});
};
var escaper = '<span tabindex="-1"></span>';
t.attr({
'tabindex':'0'
}).focus(function() {
enterNavEl();
}).blur(function() {
$(document).off('keypress.activateColumnNav');
}).append(genItemsHtml(args.items, $.makeId())).after(escaper).before(escaper);
});
};
$(document).ready(function() {
var list = {
'heading': 'Column 1',
'items': [{
'label': 'one',
'childList': {
'heading': 'one children',
'items': [{
'label': 'one.one'
}, {
'label': 'one.two',
'childList': {
'heading': 'one.two children',
'items': [{
'label': 'one.two.one'
}, {
'label': 'one.two.two'
}, {
'label': 'one.two.three'
}, {
'label': 'one.two.four'
}, {
'label': 'one.two.five (click me)',
'action': function() {
alert('You clicked on one.two.five');
}
}]
}
}, {
'label': 'one.three'
}, {
'label': 'one.four'
}]
}
}, {
'label': 'two'
}, {
'label': 'three',
'childList': {
'heading': 'three children',
'items': [{
'label': 'three.one'
}, {
'label': 'three.two'
}, {
'label': 'three.three'
}, {
'label': 'three.four'
}]
}
}, {
'label': 'four (clickable)',
'action': function() {
alert('Thanks for clicking!');
}
}]
};
$('.nav-list').navlist({
items: list,
growFunction: function() {
console.log('growing');
},
shrinkFunction: function() {
console.log('shrinking');
},
width: '170px',
height: '200px'
});
});
</script>
Copy