Add keyboard navigation and basic accessibility improvements to the tagedit widget
This commit is contained in:
parent
89ece32c78
commit
0e720bd7ec
1 changed files with 86 additions and 14 deletions
|
@ -11,7 +11,7 @@
|
||||||
* JavaScript code in this file.
|
* JavaScript code in this file.
|
||||||
*
|
*
|
||||||
* Copyright (c) 2010 Oliver Albrecht <info@webwork-albrecht.de>
|
* Copyright (c) 2010 Oliver Albrecht <info@webwork-albrecht.de>
|
||||||
* Copyright (c) 2012 Thomas Brüderli <thomas@roundcube.net>
|
* Copyright (c) 2014 Thomas Brüderli <thomas@roundcube.net>
|
||||||
*
|
*
|
||||||
* Licensed under the MIT licenses
|
* Licensed under the MIT licenses
|
||||||
*
|
*
|
||||||
|
@ -38,7 +38,7 @@
|
||||||
* for the JavaScript code in this file.
|
* for the JavaScript code in this file.
|
||||||
*
|
*
|
||||||
* @author Oliver Albrecht Mial: info@webwork-albrecht.de Twitter: @webworka
|
* @author Oliver Albrecht Mial: info@webwork-albrecht.de Twitter: @webworka
|
||||||
* @version 1.5.1 (10/2013)
|
* @version 1.5.2 (06/2014)
|
||||||
* Requires: jQuery v1.4+, jQueryUI v1.8+, jQuerry.autoGrowInput
|
* Requires: jQuery v1.4+, jQueryUI v1.8+, jQuerry.autoGrowInput
|
||||||
*
|
*
|
||||||
* Example of usage:
|
* Example of usage:
|
||||||
|
@ -125,6 +125,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
var elements = this;
|
var elements = this;
|
||||||
|
var focusItem = null;
|
||||||
|
|
||||||
var baseNameRegexp = new RegExp("^(.*)\\[([0-9]*?("+options.deletedPostfix+"|"+options.addedPostfix+")?)?\]$", "i");
|
var baseNameRegexp = new RegExp("^(.*)\\[([0-9]*?("+options.deletedPostfix+"|"+options.addedPostfix+")?)?\]$", "i");
|
||||||
|
|
||||||
|
@ -153,17 +154,18 @@
|
||||||
function inputsToList() {
|
function inputsToList() {
|
||||||
var html = '<ul class="tagedit-list '+options.additionalListClass+'">';
|
var html = '<ul class="tagedit-list '+options.additionalListClass+'">';
|
||||||
|
|
||||||
elements.each(function() {
|
elements.each(function(i) {
|
||||||
var element_name = $(this).attr('name').match(baseNameRegexp);
|
var element_name = $(this).attr('name').match(baseNameRegexp);
|
||||||
if(element_name && element_name.length == 4 && (options.deleteEmptyItems == false || $(this).val().length > 0)) {
|
if(element_name && element_name.length == 4 && (options.deleteEmptyItems == false || $(this).val().length > 0)) {
|
||||||
if(element_name[1].length > 0) {
|
if(element_name[1].length > 0) {
|
||||||
var elementId = typeof element_name[2] != 'undefined'? element_name[2]: '';
|
var elementId = typeof element_name[2] != 'undefined'? element_name[2]: '',
|
||||||
|
domId = 'tagedit-' + baseName + '-' + (elementId || i);
|
||||||
|
|
||||||
html += '<li class="tagedit-listelement tagedit-listelement-old">';
|
html += '<li class="tagedit-listelement tagedit-listelement-old" aria-labelledby="'+domId+'">';
|
||||||
html += '<span dir="'+options.direction+'">' + $(this).val() + '</span>';
|
html += '<span dir="'+options.direction+'" id="'+domId+'">' + $(this).val() + '</span>';
|
||||||
html += '<input type="hidden" name="'+baseName+'['+elementId+']" value="'+$(this).val()+'" />';
|
html += '<input type="hidden" name="'+baseName+'['+elementId+']" value="'+$(this).val()+'" />';
|
||||||
if (options.allowDelete)
|
if (options.allowDelete)
|
||||||
html += '<a class="tagedit-close" title="'+options.texts.removeLinkTitle+'">x</a>';
|
html += '<a class="tagedit-close" title="'+options.texts.removeLinkTitle+'" aria-label="'+options.texts.removeLinkTitle+' '+$(this).val()+'">x</a>';
|
||||||
html += '</li>';
|
html += '</li>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -215,12 +217,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if(options.allowAdd == true || oldValue) {
|
if(options.allowAdd == true || oldValue) {
|
||||||
|
var domId = 'tagedit-' + baseName + '-' + id;
|
||||||
// Make a new tag in front the input
|
// Make a new tag in front the input
|
||||||
html = '<li class="tagedit-listelement tagedit-listelement-old">';
|
html = '<li class="tagedit-listelement tagedit-listelement-old" aria-labelledby="'+domId+'">';
|
||||||
html += '<span dir="'+options.direction+'">' + $(this).val() + '</span>';
|
html += '<span dir="'+options.direction+'" id="'+domId+'">' + $(this).val() + '</span>';
|
||||||
var name = oldValue? baseName + '['+id+options.addedPostfix+']' : baseName + '[]';
|
var name = oldValue? baseName + '['+id+options.addedPostfix+']' : baseName + '[]';
|
||||||
html += '<input type="hidden" name="'+name+'" value="'+$(this).val()+'" />';
|
html += '<input type="hidden" name="'+name+'" value="'+$(this).val()+'" />';
|
||||||
html += '<a class="tagedit-close" title="'+options.texts.removeLinkTitle+'">x</a>';
|
html += '<a class="tagedit-close" title="'+options.texts.removeLinkTitle+'" aria-label="'+options.texts.removeLinkTitle+' '+$(this).val()+'">x</a>';
|
||||||
html += '</li>';
|
html += '</li>';
|
||||||
|
|
||||||
$(this).parent().before(html);
|
$(this).parent().before(html);
|
||||||
|
@ -239,8 +242,19 @@
|
||||||
var code = event.keyCode > 0? event.keyCode : event.which;
|
var code = event.keyCode > 0? event.keyCode : event.which;
|
||||||
|
|
||||||
switch(code) {
|
switch(code) {
|
||||||
|
case 46:
|
||||||
|
if (!focusItem)
|
||||||
|
break;
|
||||||
case 8: // BACKSPACE
|
case 8: // BACKSPACE
|
||||||
if($(this).val().length == 0) {
|
if(focusItem) {
|
||||||
|
focusItem.fadeOut(options.animSpeed, function() {
|
||||||
|
$(this).remove();
|
||||||
|
})
|
||||||
|
unfocusItem();
|
||||||
|
event.preventDefault();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if($(this).val().length == 0) {
|
||||||
// delete Last Tag
|
// delete Last Tag
|
||||||
var elementToRemove = elements.find('li.tagedit-listelement-old').last();
|
var elementToRemove = elements.find('li.tagedit-listelement-old').last();
|
||||||
elementToRemove.fadeOut(options.animSpeed, function() {elementToRemove.remove();})
|
elementToRemove.fadeOut(options.animSpeed, function() {elementToRemove.remove();})
|
||||||
|
@ -255,6 +269,40 @@
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 37: // LEFT
|
||||||
|
case 39: // RIGHT
|
||||||
|
if($(this).val().length == 0) {
|
||||||
|
// select previous Tag
|
||||||
|
var inc = code == 37 ? -1 : 1,
|
||||||
|
items = elements.find('li.tagedit-listelement-old')
|
||||||
|
x = items.length, next = 0;
|
||||||
|
items.each(function(i, elem) {
|
||||||
|
if ($(elem).hasClass('tagedit-listelement-focus')) {
|
||||||
|
x = i;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
unfocusItem();
|
||||||
|
next = Math.max(0, x + inc);
|
||||||
|
if (items.get(next)) {
|
||||||
|
focusItem = items.eq(next).addClass('tagedit-listelement-focus');
|
||||||
|
$(this).attr('aria-activedescendant', focusItem.attr('aria-labelledby'))
|
||||||
|
|
||||||
|
if(options.autocompleteOptions.source != false) {
|
||||||
|
$(this).autocomplete('close').autocomplete('disable');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// ignore input if an item is focused
|
||||||
|
if (focusItem !== null) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.bubble = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
|
@ -267,6 +315,9 @@
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
else if($(this).val().length > 0){
|
||||||
|
unfocusItem();
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
.bind('paste', function(e){
|
.bind('paste', function(e){
|
||||||
|
@ -287,9 +338,15 @@
|
||||||
var input = $(this);
|
var input = $(this);
|
||||||
$(this).data('blurtimer', window.setTimeout(function() {input.val('');}, 500));
|
$(this).data('blurtimer', window.setTimeout(function() {input.val('');}, 500));
|
||||||
}
|
}
|
||||||
|
unfocusItem();
|
||||||
|
// restore tabindex when widget looses focus
|
||||||
|
if (options.tabindex)
|
||||||
|
elements.attr('tabindex', options.tabindex);
|
||||||
})
|
})
|
||||||
.focus(function() {
|
.focus(function() {
|
||||||
window.clearTimeout($(this).data('blurtimer'));
|
window.clearTimeout($(this).data('blurtimer'));
|
||||||
|
// remove tabindex on <ul> because #tagedit-input now has it
|
||||||
|
elements.attr('tabindex', '-1');
|
||||||
});
|
});
|
||||||
|
|
||||||
if(options.autocompleteOptions.source != false) {
|
if(options.autocompleteOptions.source != false) {
|
||||||
|
@ -302,6 +359,7 @@
|
||||||
case 'A':
|
case 'A':
|
||||||
$(event.target).parent().fadeOut(options.animSpeed, function() {
|
$(event.target).parent().fadeOut(options.animSpeed, function() {
|
||||||
$(event.target).parent().remove();
|
$(event.target).parent().remove();
|
||||||
|
elements.find('#tagedit-input').focus();
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'INPUT':
|
case 'INPUT':
|
||||||
|
@ -324,6 +382,20 @@
|
||||||
.focus(function(e){ $(this).click(); })
|
.focus(function(e){ $(this).click(); })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove class and reference to currently focused tag item
|
||||||
|
*/
|
||||||
|
function unfocusItem() {
|
||||||
|
if(focusItem){
|
||||||
|
if(options.autocompleteOptions.source != false) {
|
||||||
|
elements.find('#tagedit-input').autocomplete('enable');
|
||||||
|
}
|
||||||
|
focusItem.removeClass('tagedit-listelement-focus');
|
||||||
|
focusItem = null;
|
||||||
|
elements.find('#tagedit-input').removeAttr('aria-activedescendant');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets all Actions and events for editing an Existing Tag.
|
* Sets all Actions and events for editing an Existing Tag.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Add table
Reference in a new issue