Improve accessibility & add unsorted header class

This commit is contained in:
Mottie 2014-01-11 13:08:37 -06:00
parent 3256926f29
commit 0e438e4bbd
3 changed files with 93 additions and 24 deletions

View File

@ -115,10 +115,12 @@
r = 'removeClass',
d = p.cssDisabled,
dis = !!disable,
first = ( dis || p.page === 0 ),
last = ( dis || p.page === tp - 1 || p.totalPages === 0 ),
tp = Math.min( p.totalPages, p.filteredPages );
if ( p.updateArrows ) {
p.$container.find(p.cssFirst + ',' + p.cssPrev)[ ( dis || p.page === 0 ) ? a : r ](d);
p.$container.find(p.cssNext + ',' + p.cssLast)[ ( dis || p.page === tp - 1 || p.totalPages === 0 ) ? a : r ](d);
p.$container.find(p.cssFirst + ',' + p.cssPrev)[ first ? a : r ](d).attr('aria-disabled', first);
p.$container.find(p.cssNext + ',' + p.cssLast)[ last ? a : r ](d).attr('aria-disabled', last);
}
},
@ -251,7 +253,8 @@
if (c.debug) {
ts.log('Ajax Error', xhr, exception);
}
$err = $('<tr class="' + p.cssErrorRow + '"><td style="text-align:center;" colspan="' + hl + '">' + (
$err = $('<tr class="' + p.cssErrorRow + '" role="alert" aria-live="assertive">' +
'<td style="text-align:center;" colspan="' + hl + '">' + (
xhr.status === 0 ? 'Not connected, verify Network' :
xhr.status === 404 ? 'Requested page not found [404]' :
xhr.status === 500 ? 'Internal Server Error [500]' :
@ -470,7 +473,10 @@
p.page = 0;
p.size = p.totalRows;
p.totalPages = 1;
$(table).addClass('pagerDisabled').find('tr.pagerSavedHeightSpacer').remove();
$(table)
.addClass('pagerDisabled')
.removeAttr('aria-describedby')
.find('tr.pagerSavedHeightSpacer').remove();
renderTable(table, table.config.rowsCopy, p);
if (table.config.debug) {
ts.log('pager disabled');
@ -478,7 +484,7 @@
}
// disable size selector
p.$size.add(p.$goto).each(function(){
$(this).addClass(p.cssDisabled)[0].disabled = true;
$(this).attr('aria-disabled', 'true').addClass(p.cssDisabled)[0].disabled = true;
});
},
@ -568,7 +574,7 @@
p.$size.add(p.$goto).removeClass(p.cssDisabled).removeAttr('disabled').attr('aria-disabled', 'false');
p.isDisabled = false;
p.page = $.data(table, 'pagerLastPage') || p.page || 0;
p.size = $.data(table, 'pagerLastSize') || parseInt(pg.find('option[selected]').val(), 10) || p.size || 10;
p.size = $.data(table, 'pagerLastSize') || parseInt(p.$size.find('option[selected]').val(), 10) || p.size || 10;
p.$size.val(p.size); // set page size
p.totalPages = Math.ceil( Math.min( p.totalRows, p.filteredRows ) / p.size );
// if table id exists, include page display with aria info
@ -678,6 +684,7 @@
ctrls = [ p.cssFirst, p.cssPrev, p.cssNext, p.cssLast ];
fxn = [ moveToFirstPage, moveToPrevPage, moveToNextPage, moveToLastPage ];
pager.find(ctrls.join(','))
.attr("tabindex", 0)
.unbind('click.pager')
.bind('click.pager', function(e){
e.stopPropagation();

View File

@ -82,6 +82,7 @@
tableClass : '',
cssAsc : '',
cssDesc : '',
cssNone : '',
cssHeader : '',
cssHeaderRow : '',
cssProcessing : '', // processing icon applied to header during sort/filter
@ -120,7 +121,18 @@
info : 'tablesorter-infoOnly',
processing : 'tablesorter-processing',
sortAsc : 'tablesorter-headerAsc',
sortDesc : 'tablesorter-headerDesc'
sortDesc : 'tablesorter-headerDesc',
sortNone : 'tablesorter-headerUnSorted'
};
// labels applied to sortable headers for accessibility (aria) support
ts.language = {
sortAsc : 'Ascending sort applied, ',
sortDesc : 'Descending sort applied, ',
sortNone : 'No sort applied, ',
nextAsc : 'activate to apply an ascending sort',
nextDesc : 'activate to apply a descending sort',
nextNone : 'activate to remove the sort'
};
/* debuging utils */
@ -443,9 +455,12 @@
// add cell to headerList
c.headerList[index] = this;
// add to parent in case there are multiple rows
$t.parent().addClass(ts.css.headerRow + ' ' + c.cssHeaderRow);
$t.parent().addClass(ts.css.headerRow + ' ' + c.cssHeaderRow).attr('role', 'row');
// allow keyboard cursor to focus on element
if (c.tabIndex) { $t.attr("tabindex", 0); }
}).attr({
scope: 'col',
role : 'columnheader'
});
// enable/disable sorting
updateHeader(table);
@ -467,11 +482,20 @@
}
function updateHeader(table) {
var s, c = table.config;
var s, $th, c = table.config;
c.$headers.each(function(index, th){
$th = $(th);
s = ts.getData( th, c.headers[index], 'sorter' ) === 'false';
th.sortDisabled = s;
$(th)[ s ? 'addClass' : 'removeClass' ]('sorter-false');
$th[ s ? 'addClass' : 'removeClass' ]('sorter-false').attr('aria-disabled', '' + s);
// aria-controls - requires table ID
if (table.id) {
if (s) {
$th.removeAttr('aria-controls');
} else {
$th.attr('aria-controls', table.id);
}
}
});
}
@ -479,11 +503,15 @@
var f, i, j, l,
c = table.config,
list = c.sortList,
none = ts.css.sortNone + ' ' + c.cssNone,
css = [ts.css.sortAsc + ' ' + c.cssAsc, ts.css.sortDesc + ' ' + c.cssDesc],
aria = ['ascending', 'descending'],
// find the footer
$t = $(table).find('tfoot tr').children().removeClass(css.join(' '));
// remove all header information
c.$headers.removeClass(css.join(' '));
c.$headers
.removeClass(css.join(' '))
.addClass(none).attr('aria-sort', 'none');
l = list.length;
for (i = 0; i < l; i++) {
// direction = 2 means reset!
@ -493,7 +521,7 @@
if (f.length) {
for (j = 0; j < f.length; j++) {
if (!f[j].sortDisabled) {
f.eq(j).addClass(css[list[i][1]]);
f.eq(j).removeClass(none).addClass(css[list[i][1]]).attr('aria-sort', aria[list[i][1]]);
// add sorted class to footer, if it exists
if ($t.length) {
$t.filter('[data-column="' + list[i][0] + '"]').eq(j).addClass(css[list[i][1]]);
@ -503,6 +531,15 @@
}
}
}
// add verbose aria labels
c.$headers.not('.sorter-false').each(function(){
var $this = $(this),
nextSort = this.order[(this.count + 1) % (c.sortReset ? 3 : 2)],
txt = $this.text() + ': ' +
ts.language[ $this.hasClass(ts.css.sortAsc) ? 'sortAsc' : $this.hasClass(ts.css.sortDesc) ? 'sortDesc' : 'sortNone' ] +
ts.language[ nextSort === 0 ? 'nextAsc' : nextSort === 1 ? 'nextDesc' : 'nextNone' ];
$this.attr('aria-label', txt );
});
}
// automatically add col group, and column sizes if set
@ -901,8 +938,17 @@
if (!/tablesorter\-/.test($table.attr('class'))) {
k = (c.theme !== '' ? ' tablesorter-' + c.theme : '');
}
c.$table = $table.addClass(ts.css.table + ' ' + c.tableClass + k);
c.$tbodies = $table.children('tbody:not(.' + c.cssInfoBlock + ')');
c.$table = $table
.addClass(ts.css.table + ' ' + c.tableClass + k)
.attr({ role : 'grid'});
c.$tbodies = $table.children('tbody:not(.' + c.cssInfoBlock + ')').attr({
'aria-live' : 'polite',
'aria-relevant' : 'all'
});
//
if (c.$table.find('caption').length) {
c.$table.attr('aria-labelledby', 'theCaption');
}
c.widgetInit = {}; // keep a list of initialized widgets
// build headers
buildHeaders(table);
@ -929,9 +975,12 @@
// if user has supplied a sort list to constructor
if (c.sortList.length > 0) {
$table.trigger("sorton", [c.sortList, {}, !c.initWidgets]);
} else if (c.initWidgets) {
// apply widget format
ts.applyWidget(table);
} else {
setHeadersCss(table);
if (c.initWidgets) {
// apply widget format
ts.applyWidget(table);
}
}
// show processesing icon

View File

@ -251,6 +251,7 @@ tsp = ts.pager = {
ctrls = [ s.first, s.prev, s.next, s.last ];
fxn = [ 'moveToFirstPage', 'moveToPrevPage', 'moveToNextPage', 'moveToLastPage' ];
p.$container.find(ctrls.join(','))
.attr("tabindex", 0)
.unbind('click.pager')
.bind('click.pager', function(e){
e.stopPropagation();
@ -296,12 +297,14 @@ tsp = ts.pager = {
pagerArrows: function(c, disable) {
var p = c.pager,
dis = !!disable,
first = dis || p.page === 0,
last = dis || p.page === tp - 1 || p.totalPages === 0,
wo = c.widgetOptions,
s = wo.pager_selectors,
tp = Math.min( p.totalPages, p.filteredPages );
if ( wo.pager_updateArrows ) {
p.$container.find(s.first + ',' + s.prev).toggleClass(wo.pager_css.disabled, dis || p.page === 0);
p.$container.find(s.next + ',' + s.last).toggleClass(wo.pager_css.disabled, dis || p.page === tp - 1 || p.totalPages === 0);
p.$container.find(s.first + ',' + s.prev).toggleClass(wo.pager_css.disabled, first).attr('aria-disabled', first);
p.$container.find(s.next + ',' + s.last).toggleClass(wo.pager_css.disabled, last).attr('aria-disabled', last);
}
},
@ -313,7 +316,7 @@ tsp = ts.pager = {
t = (c.widgetOptions && c.widgetOptions.filter_filteredRow || 'filtered') + ',' + c.selectorRemove +
(wo.pager_countChildRows ? '' : ',.' + c.cssChildRow),
sz = p.size || 10; // don't allow dividing by zero
p.$size.add(p.$goto).removeClass(wo.pager_css.disabled).removeAttr('disabled');
p.$size.add(p.$goto).removeClass(wo.pager_css.disabled).removeAttr('disabled').attr('aria-disabled', 'false');
p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method
p.filteredRows = (f) ? c.$tbodies.eq(0).children('tr').not('.' + t).length : p.totalRows;
p.filteredPages = (f) ? Math.ceil( p.filteredRows / sz ) || 1 : p.totalPages;
@ -440,7 +443,8 @@ tsp = ts.pager = {
if (c.debug) {
ts.log('Ajax Error', xhr, exception);
}
$err = $('<tr class="' + wo.pager_css.errorRow + '"><td style="text-align:center;" colspan="' +
$err = $('<tr class="' + wo.pager_css.errorRow + '" role="alert" aria-live="assertive">' +
'<td style="text-align:center;" colspan="' +
hl + '">' + exception.message + ' (' + xhr.status + ')</td></tr>')
.click(function(){
$(this).remove();
@ -662,7 +666,10 @@ tsp = ts.pager = {
p.page = 0;
p.size = p.totalRows;
p.totalPages = 1;
c.$table.addClass('pagerDisabled').find('tr.pagerSavedHeightSpacer').remove();
c.$table
.addClass('pagerDisabled')
.removeAttr('aria-describedby')
.find('tr.pagerSavedHeightSpacer').remove();
tsp.renderTable(table, c.rowsCopy);
if (c.debug) {
ts.log('pager disabled');
@ -670,7 +677,7 @@ tsp = ts.pager = {
}
// disable size selector
p.$size.add(p.$goto).each(function(){
$(this).addClass(wo.pager_css.disabled)[0].disabled = true;
$(this).attr('aria-disabled', 'true').addClass(wo.pager_css.disabled)[0].disabled = true;
});
c.$table.trigger('applyWidgets');
},
@ -758,13 +765,19 @@ tsp = ts.pager = {
},
enablePager: function(table, c, triggered){
var p = c.pager;
var info, p = c.pager;
p.isDisabled = false;
p.page = $.data(table, 'pagerLastPage') || p.page || 0;
p.size = $.data(table, 'pagerLastSize') || parseInt(p.$size.find('option[selected]').val(), 10) || p.size || 10;
p.$size.val(p.size); // set page size
p.totalPages = Math.ceil( Math.min( p.totalRows, p.filteredRows ) / p.size );
c.$table.removeClass('pagerDisabled');
// if table id exists, include page display with aria info
if ( table.id ) {
info = table.id + '_pager_info';
p.$container.find(wo.pager_selectors.pageDisplay).attr('id', info);
c.$table.attr('aria-describedby', info);
}
if ( triggered ) {
c.$table.trigger('updateRows');
tsp.setPageSize(table, p.size, c);