mirror of
https://github.com/Mottie/tablesorter.git
synced 2025-01-12 15:24:21 +00:00
Improve accessibility & add unsorted header class
This commit is contained in:
parent
3256926f29
commit
0e438e4bbd
@ -115,10 +115,12 @@
|
|||||||
r = 'removeClass',
|
r = 'removeClass',
|
||||||
d = p.cssDisabled,
|
d = p.cssDisabled,
|
||||||
dis = !!disable,
|
dis = !!disable,
|
||||||
|
first = ( dis || p.page === 0 ),
|
||||||
|
last = ( dis || p.page === tp - 1 || p.totalPages === 0 ),
|
||||||
tp = Math.min( p.totalPages, p.filteredPages );
|
tp = Math.min( p.totalPages, p.filteredPages );
|
||||||
if ( p.updateArrows ) {
|
if ( p.updateArrows ) {
|
||||||
p.$container.find(p.cssFirst + ',' + p.cssPrev)[ ( dis || p.page === 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)[ ( dis || p.page === tp - 1 || p.totalPages === 0 ) ? a : r ](d);
|
p.$container.find(p.cssNext + ',' + p.cssLast)[ last ? a : r ](d).attr('aria-disabled', last);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -251,7 +253,8 @@
|
|||||||
if (c.debug) {
|
if (c.debug) {
|
||||||
ts.log('Ajax Error', xhr, exception);
|
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 === 0 ? 'Not connected, verify Network' :
|
||||||
xhr.status === 404 ? 'Requested page not found [404]' :
|
xhr.status === 404 ? 'Requested page not found [404]' :
|
||||||
xhr.status === 500 ? 'Internal Server Error [500]' :
|
xhr.status === 500 ? 'Internal Server Error [500]' :
|
||||||
@ -470,7 +473,10 @@
|
|||||||
p.page = 0;
|
p.page = 0;
|
||||||
p.size = p.totalRows;
|
p.size = p.totalRows;
|
||||||
p.totalPages = 1;
|
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);
|
renderTable(table, table.config.rowsCopy, p);
|
||||||
if (table.config.debug) {
|
if (table.config.debug) {
|
||||||
ts.log('pager disabled');
|
ts.log('pager disabled');
|
||||||
@ -478,7 +484,7 @@
|
|||||||
}
|
}
|
||||||
// disable size selector
|
// disable size selector
|
||||||
p.$size.add(p.$goto).each(function(){
|
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.$size.add(p.$goto).removeClass(p.cssDisabled).removeAttr('disabled').attr('aria-disabled', 'false');
|
||||||
p.isDisabled = false;
|
p.isDisabled = false;
|
||||||
p.page = $.data(table, 'pagerLastPage') || p.page || 0;
|
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.$size.val(p.size); // set page size
|
||||||
p.totalPages = Math.ceil( Math.min( p.totalRows, p.filteredRows ) / p.size );
|
p.totalPages = Math.ceil( Math.min( p.totalRows, p.filteredRows ) / p.size );
|
||||||
// if table id exists, include page display with aria info
|
// if table id exists, include page display with aria info
|
||||||
@ -678,6 +684,7 @@
|
|||||||
ctrls = [ p.cssFirst, p.cssPrev, p.cssNext, p.cssLast ];
|
ctrls = [ p.cssFirst, p.cssPrev, p.cssNext, p.cssLast ];
|
||||||
fxn = [ moveToFirstPage, moveToPrevPage, moveToNextPage, moveToLastPage ];
|
fxn = [ moveToFirstPage, moveToPrevPage, moveToNextPage, moveToLastPage ];
|
||||||
pager.find(ctrls.join(','))
|
pager.find(ctrls.join(','))
|
||||||
|
.attr("tabindex", 0)
|
||||||
.unbind('click.pager')
|
.unbind('click.pager')
|
||||||
.bind('click.pager', function(e){
|
.bind('click.pager', function(e){
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
@ -82,6 +82,7 @@
|
|||||||
tableClass : '',
|
tableClass : '',
|
||||||
cssAsc : '',
|
cssAsc : '',
|
||||||
cssDesc : '',
|
cssDesc : '',
|
||||||
|
cssNone : '',
|
||||||
cssHeader : '',
|
cssHeader : '',
|
||||||
cssHeaderRow : '',
|
cssHeaderRow : '',
|
||||||
cssProcessing : '', // processing icon applied to header during sort/filter
|
cssProcessing : '', // processing icon applied to header during sort/filter
|
||||||
@ -120,7 +121,18 @@
|
|||||||
info : 'tablesorter-infoOnly',
|
info : 'tablesorter-infoOnly',
|
||||||
processing : 'tablesorter-processing',
|
processing : 'tablesorter-processing',
|
||||||
sortAsc : 'tablesorter-headerAsc',
|
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 */
|
/* debuging utils */
|
||||||
@ -443,9 +455,12 @@
|
|||||||
// add cell to headerList
|
// add cell to headerList
|
||||||
c.headerList[index] = this;
|
c.headerList[index] = this;
|
||||||
// add to parent in case there are multiple rows
|
// 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
|
// allow keyboard cursor to focus on element
|
||||||
if (c.tabIndex) { $t.attr("tabindex", 0); }
|
if (c.tabIndex) { $t.attr("tabindex", 0); }
|
||||||
|
}).attr({
|
||||||
|
scope: 'col',
|
||||||
|
role : 'columnheader'
|
||||||
});
|
});
|
||||||
// enable/disable sorting
|
// enable/disable sorting
|
||||||
updateHeader(table);
|
updateHeader(table);
|
||||||
@ -467,11 +482,20 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateHeader(table) {
|
function updateHeader(table) {
|
||||||
var s, c = table.config;
|
var s, $th, c = table.config;
|
||||||
c.$headers.each(function(index, th){
|
c.$headers.each(function(index, th){
|
||||||
|
$th = $(th);
|
||||||
s = ts.getData( th, c.headers[index], 'sorter' ) === 'false';
|
s = ts.getData( th, c.headers[index], 'sorter' ) === 'false';
|
||||||
th.sortDisabled = s;
|
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,
|
var f, i, j, l,
|
||||||
c = table.config,
|
c = table.config,
|
||||||
list = c.sortList,
|
list = c.sortList,
|
||||||
|
none = ts.css.sortNone + ' ' + c.cssNone,
|
||||||
css = [ts.css.sortAsc + ' ' + c.cssAsc, ts.css.sortDesc + ' ' + c.cssDesc],
|
css = [ts.css.sortAsc + ' ' + c.cssAsc, ts.css.sortDesc + ' ' + c.cssDesc],
|
||||||
|
aria = ['ascending', 'descending'],
|
||||||
// find the footer
|
// find the footer
|
||||||
$t = $(table).find('tfoot tr').children().removeClass(css.join(' '));
|
$t = $(table).find('tfoot tr').children().removeClass(css.join(' '));
|
||||||
// remove all header information
|
// remove all header information
|
||||||
c.$headers.removeClass(css.join(' '));
|
c.$headers
|
||||||
|
.removeClass(css.join(' '))
|
||||||
|
.addClass(none).attr('aria-sort', 'none');
|
||||||
l = list.length;
|
l = list.length;
|
||||||
for (i = 0; i < l; i++) {
|
for (i = 0; i < l; i++) {
|
||||||
// direction = 2 means reset!
|
// direction = 2 means reset!
|
||||||
@ -493,7 +521,7 @@
|
|||||||
if (f.length) {
|
if (f.length) {
|
||||||
for (j = 0; j < f.length; j++) {
|
for (j = 0; j < f.length; j++) {
|
||||||
if (!f[j].sortDisabled) {
|
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
|
// add sorted class to footer, if it exists
|
||||||
if ($t.length) {
|
if ($t.length) {
|
||||||
$t.filter('[data-column="' + list[i][0] + '"]').eq(j).addClass(css[list[i][1]]);
|
$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
|
// automatically add col group, and column sizes if set
|
||||||
@ -901,8 +938,17 @@
|
|||||||
if (!/tablesorter\-/.test($table.attr('class'))) {
|
if (!/tablesorter\-/.test($table.attr('class'))) {
|
||||||
k = (c.theme !== '' ? ' tablesorter-' + c.theme : '');
|
k = (c.theme !== '' ? ' tablesorter-' + c.theme : '');
|
||||||
}
|
}
|
||||||
c.$table = $table.addClass(ts.css.table + ' ' + c.tableClass + k);
|
c.$table = $table
|
||||||
c.$tbodies = $table.children('tbody:not(.' + c.cssInfoBlock + ')');
|
.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
|
c.widgetInit = {}; // keep a list of initialized widgets
|
||||||
// build headers
|
// build headers
|
||||||
buildHeaders(table);
|
buildHeaders(table);
|
||||||
@ -929,9 +975,12 @@
|
|||||||
// if user has supplied a sort list to constructor
|
// if user has supplied a sort list to constructor
|
||||||
if (c.sortList.length > 0) {
|
if (c.sortList.length > 0) {
|
||||||
$table.trigger("sorton", [c.sortList, {}, !c.initWidgets]);
|
$table.trigger("sorton", [c.sortList, {}, !c.initWidgets]);
|
||||||
} else if (c.initWidgets) {
|
} else {
|
||||||
// apply widget format
|
setHeadersCss(table);
|
||||||
ts.applyWidget(table);
|
if (c.initWidgets) {
|
||||||
|
// apply widget format
|
||||||
|
ts.applyWidget(table);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// show processesing icon
|
// show processesing icon
|
||||||
|
@ -251,6 +251,7 @@ tsp = ts.pager = {
|
|||||||
ctrls = [ s.first, s.prev, s.next, s.last ];
|
ctrls = [ s.first, s.prev, s.next, s.last ];
|
||||||
fxn = [ 'moveToFirstPage', 'moveToPrevPage', 'moveToNextPage', 'moveToLastPage' ];
|
fxn = [ 'moveToFirstPage', 'moveToPrevPage', 'moveToNextPage', 'moveToLastPage' ];
|
||||||
p.$container.find(ctrls.join(','))
|
p.$container.find(ctrls.join(','))
|
||||||
|
.attr("tabindex", 0)
|
||||||
.unbind('click.pager')
|
.unbind('click.pager')
|
||||||
.bind('click.pager', function(e){
|
.bind('click.pager', function(e){
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@ -296,12 +297,14 @@ tsp = ts.pager = {
|
|||||||
pagerArrows: function(c, disable) {
|
pagerArrows: function(c, disable) {
|
||||||
var p = c.pager,
|
var p = c.pager,
|
||||||
dis = !!disable,
|
dis = !!disable,
|
||||||
|
first = dis || p.page === 0,
|
||||||
|
last = dis || p.page === tp - 1 || p.totalPages === 0,
|
||||||
wo = c.widgetOptions,
|
wo = c.widgetOptions,
|
||||||
s = wo.pager_selectors,
|
s = wo.pager_selectors,
|
||||||
tp = Math.min( p.totalPages, p.filteredPages );
|
tp = Math.min( p.totalPages, p.filteredPages );
|
||||||
if ( wo.pager_updateArrows ) {
|
if ( wo.pager_updateArrows ) {
|
||||||
p.$container.find(s.first + ',' + s.prev).toggleClass(wo.pager_css.disabled, dis || p.page === 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, dis || p.page === tp - 1 || p.totalPages === 0);
|
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 +
|
t = (c.widgetOptions && c.widgetOptions.filter_filteredRow || 'filtered') + ',' + c.selectorRemove +
|
||||||
(wo.pager_countChildRows ? '' : ',.' + c.cssChildRow),
|
(wo.pager_countChildRows ? '' : ',.' + c.cssChildRow),
|
||||||
sz = p.size || 10; // don't allow dividing by zero
|
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.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.filteredRows = (f) ? c.$tbodies.eq(0).children('tr').not('.' + t).length : p.totalRows;
|
||||||
p.filteredPages = (f) ? Math.ceil( p.filteredRows / sz ) || 1 : p.totalPages;
|
p.filteredPages = (f) ? Math.ceil( p.filteredRows / sz ) || 1 : p.totalPages;
|
||||||
@ -440,7 +443,8 @@ tsp = ts.pager = {
|
|||||||
if (c.debug) {
|
if (c.debug) {
|
||||||
ts.log('Ajax Error', xhr, exception);
|
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>')
|
hl + '">' + exception.message + ' (' + xhr.status + ')</td></tr>')
|
||||||
.click(function(){
|
.click(function(){
|
||||||
$(this).remove();
|
$(this).remove();
|
||||||
@ -662,7 +666,10 @@ tsp = ts.pager = {
|
|||||||
p.page = 0;
|
p.page = 0;
|
||||||
p.size = p.totalRows;
|
p.size = p.totalRows;
|
||||||
p.totalPages = 1;
|
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);
|
tsp.renderTable(table, c.rowsCopy);
|
||||||
if (c.debug) {
|
if (c.debug) {
|
||||||
ts.log('pager disabled');
|
ts.log('pager disabled');
|
||||||
@ -670,7 +677,7 @@ tsp = ts.pager = {
|
|||||||
}
|
}
|
||||||
// disable size selector
|
// disable size selector
|
||||||
p.$size.add(p.$goto).each(function(){
|
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');
|
c.$table.trigger('applyWidgets');
|
||||||
},
|
},
|
||||||
@ -758,13 +765,19 @@ tsp = ts.pager = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
enablePager: function(table, c, triggered){
|
enablePager: function(table, c, triggered){
|
||||||
var p = c.pager;
|
var info, p = c.pager;
|
||||||
p.isDisabled = false;
|
p.isDisabled = false;
|
||||||
p.page = $.data(table, 'pagerLastPage') || p.page || 0;
|
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 = $.data(table, 'pagerLastSize') || parseInt(p.$size.find('option[selected]').val(), 10) || p.size || 10;
|
||||||
p.$size.val(p.size); // set page size
|
p.$size.val(p.size); // set page size
|
||||||
p.totalPages = Math.ceil( Math.min( p.totalRows, p.filteredRows ) / p.size );
|
p.totalPages = Math.ceil( Math.min( p.totalRows, p.filteredRows ) / p.size );
|
||||||
c.$table.removeClass('pagerDisabled');
|
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 ) {
|
if ( triggered ) {
|
||||||
c.$table.trigger('updateRows');
|
c.$table.trigger('updateRows');
|
||||||
tsp.setPageSize(table, p.size, c);
|
tsp.setPageSize(table, p.size, c);
|
||||||
|
Loading…
Reference in New Issue
Block a user