Scroller: correct column alignment. Fixes #940, #937, #931 & #927

Bonus fix #932 which maintains scroll position after sort/filter
This commit is contained in:
Mottie 2015-06-21 10:09:57 -05:00
parent 36406468be
commit 7b6ebfa639
2 changed files with 113 additions and 132 deletions

View File

@ -94,18 +94,22 @@
}
});
var startFixedColumns = 2;
$('#fixed-columns-table').tablesorter({
theme: 'jui',
showProcessing: true,
headerTemplate : '{content} {icon}',
widgets: [ 'uitheme', 'zebra', 'filter', 'scroller' ],
widgetOptions : {
scroller_jumpToHeader : false,
scroller_upAfterSort : false,
// scroll tbody to top after sorting
scroller_upAfterSort: true,
// pop table header into view while scrolling up the page
scroller_jumpToHeader: true,
scroller_height : 300,
// set number of columns to fix
scroller_fixedColumns : 2,
scroller_fixedColumns : startFixedColumns,
// add a fixed column overlay for styling
scroller_addFixedOverlay : false,
// add hover highlighting to the fixed column (disable if it causes slowing)
@ -118,7 +122,7 @@
// use jQuery UI slider to change the fixed column size
$( '#slider' ).slider({
value : 2,
value : startFixedColumns,
min : 0,
max : 4,
step : 1,
@ -130,20 +134,15 @@
}
});
// update column value display
$( '.fixed-columns' ).text( startFixedColumns );
});</script>
<script>
$(function() {
$('.options').tablesorter({
theme: 'jui',
headerTemplate : '{content} {icon}',
widthFixed: true,
widgets: ['uitheme','stickyHeaders']
});
var $tbl,
$jth = $('#jth'),
$uas = $('#uas'),
var $jth = $('#jth'), // scroller_jumpToHeader toggle button
$uas = $('#uas'), // scroller_upAfterSort toggle button
themes = 'default blue green grey ice black-ice dark dropbox metro-dark',
i, o = '', t = themes.split(' ');
for (i = 0; i < t.length; i++) {
@ -171,7 +170,6 @@ $(function() {
$(window).trigger('resize');
}).change();
$tbl = $('.tablesorter-scroller-table table');
$('button').click(function(){
var jth = $jth.text() === 'true',
uas = $uas.text() === 'true';
@ -180,12 +178,15 @@ $(function() {
} else {
uas = !uas;
}
$tbl.each(function(){
this.config.widgetOptions.scroller_jumpToHeader = jth;
this.config.widgetOptions.scroller_upAfterSort = uas;
$('.tablesorter-scroller-table table').each(function(){
var c = this.config;
if (c) {
c.widgetOptions.scroller_jumpToHeader = jth;
c.widgetOptions.scroller_upAfterSort = uas;
}
});
$jth.html(jth + '');
$uas.html(uas + '');
});
return false;
});
@ -686,6 +687,12 @@ $(function() {
<tr><td>A13</td><td>John</td><td>James</td><td>16</td><td>13.89</td><td>42.1%</td><td>-13</td></tr>
<tr><td>A71</td><td>Nick</td><td>Parker</td><td>45</td><td>13.89</td><td>44%</td><td>+29</td></tr>
<tr><td>A21</td><td>Charles</td><td>Dunn</td><td>19</td><td>15.49</td><td>22%</td><td>+3</td></tr>
<tr><td>A42</td><td>Tera</td><td>Jones</td><td>83</td><td>14.19</td><td>13%</td><td>+5</td></tr>
<tr><td>A51</td><td>Paul</td><td>Daniels</td><td>76</td><td>5.99</td><td>20%</td><td>+1</td></tr>
<tr><td>A36</td><td>Harvey</td><td>Phillips</td><td>84</td><td>22.50</td><td>23%</td><td>+2</td></tr>
<tr><td>A5</td><td>James</td><td>Micheal</td><td>11</td><td>12.99</td><td>44.4%</td><td>-3</td></tr>
<tr><td>A1</td><td>Norma</td><td>Harry</td><td>43</td><td>12.39</td><td>41%</td><td>-9</td></tr>
<tr><td>A91</td><td>Charley</td><td>Duncan</td><td>22</td><td>14.44</td><td>12%</td><td>-1</td></tr>
</tbody>
</table>
</div>

View File

@ -52,7 +52,6 @@ $.extend( ts.css, {
scrollerBarSpacer : 'tablesorter-scroller-bar-spacer',
scrollerAddedHeight : 'tablesorter-scroller-added-height',
scrollerHack : 'tablesorter-scroller-scrollbar-hack',
scrollerReset : 'tablesorter-scroller-reset',
// class name on table cannot start with 'tablesorter-' or the
// suffix "scroller-rtl" will match as a theme name
scrollerRtl : 'ts-scroller-rtl'
@ -102,8 +101,6 @@ ts.window_resize = function() {
// Add extra scroller css
$( function() {
var style = '<style>' +
/* reset width to get accurate measurements after window resize */
'.' + tscss.scrollerReset + ' { width: auto !important; min-width: auto !important; max-width: auto !important; }' +
/* overall wrapper & table section wrappers */
'.' + tscss.scrollerWrap + ' { position: relative; overflow: hidden; }' +
/* add border-box sizing to all scroller widget tables; see #135 */
@ -188,21 +185,25 @@ ts.scroller = {
},
setup : function( c, wo ) {
var maxHt, tbHt, $hdr, $t, $hCells, $fCells, $tableWrap, tmp,
var maxHt, tbHt, $hdr, $t, $hCells, $fCells, $tableWrap, events, tmp,
$win = $( window ),
tsScroller = ts.scroller,
namespace = c.namespace + 'tsscroller',
$foot = $(),
// c.namespace contains a unique tablesorter ID, per table
id = c.namespace.slice( 1 ) + 'tsscroller',
$table = c.$table;
// force config.widthFixed option - this helps maintain proper alignment across cloned tables
c.widthFixed = true;
wo.scroller_calcWidths = [];
wo.scroller_isBusy = true;
// set scrollbar width & allow setting width to zero
wo.scroller_barSetWidth = wo.scroller_barWidth !== null ?
wo.scroller_barWidth :
( ts.scroller.getBarWidth() || 15 );
( tsScroller.getBarWidth() || 15 );
maxHt = wo.scroller_height || 300;
@ -274,42 +275,51 @@ ts.scroller = {
.scrollLeft( $( this ).scrollLeft() );
});
// Sorting, so scroll to top
tmp = 'sortEnd setFixedColumnSize updateComplete pagerComplete pagerInitialized columnUpdate '
.split( ' ' )
.join( namespace + ' ' );
// resize/update events
events = ( ( ts.hasWidget( c.table, 'filter' ) ? 'filterEnd' : 'tablesorter-initialized' ) +
' updateComplete pagerComplete columnUpdate ' ).split( ' ' ).join( namespace + ' ' )
$table
.off( tmp )
.off( namespace )
.on( 'sortEnd' + namespace, function() {
// Sorting, so scroll to top
if ( wo.scroller_upAfterSort ) {
$table.parent().animate({
scrollTop : 0
}, 'fast' );
}
})
.on( 'sortEnd filterEnd'.split( ' ' ).join( namespace + ' ' ), function(e) {
setTimeout(function(){
$tableWrap.trigger( 'scroll' );
}, 0);
})
.on( 'setFixedColumnSize' + namespace, function( event, size ) {
var $wrap = wo.scroller_$container;
if ( typeof size !== 'undefined' && !isNaN( size ) ) {
wo.scroller_fixedColumns = parseInt( size, 10 );
}
// remove fixed columns
ts.scroller.removeFixed( c, wo );
tsScroller.removeFixed( c, wo );
size = wo.scroller_fixedColumns;
if ( size > 0 && size < c.columns - 1 ) {
ts.scroller.updateFixed( c, wo );
tsScroller.updateFixed( c, wo );
} else if ( $wrap.hasClass( tscss.scrollerHasFix ) ) {
$wrap.removeClass( tscss.scrollerHasFix );
// resize needed to make tables full width
ts.scroller.resize( c, wo );
tsScroller.resize( c, wo );
}
})
.on( 'updateComplete pagerComplete columnUpdate '.split( ' ' ).join( namespace + ' ' ), function( event ) {
.on( events, function( event ) {
// Stop from running twice with pager
if ( ts.hasWidget( 'pager' ) && event.type === 'updateComplete' ) {
return;
}
if ( wo.scroller_fixedColumns > 0 ) {
tsScroller.updateFixed( c, wo, false );
}
// adjust column sizes after an update
ts.scroller.resize( c, wo );
tsScroller.resize( c, wo );
});
// Setup window.resizeEnd event
@ -320,7 +330,7 @@ ts.scroller = {
// IE calls resize when you modify content, so we have to unbind the resize event
// so we don't end up with an infinite loop. we can rebind after we're done.
$win.off( 'resize' + namespace, ts.window_resize );
ts.scroller.resize( c, wo );
tsScroller.resize( c, wo );
$win.on( 'resize' + namespace, ts.window_resize );
$tableWrap.trigger( 'scroll' + namespace );
});
@ -328,13 +338,15 @@ ts.scroller = {
// initialization flag
c.isScrolling = true;
ts.scroller.updateFixed( c, wo );
tsScroller.updateFixed( c, wo );
},
resize : function( c, wo ) {
if ( wo.scroller_isBusy ) { return; }
var index, borderWidth, setWidth, $hCells, $bCells, $fCells, $headers, $this, temp,
tsScroller = ts.scroller,
$container = wo.scroller_$container,
$table = c.$table,
$tableWrap = $table.parent(),
$hdr = wo.scroller_$header,
@ -342,52 +354,28 @@ ts.scroller = {
id = c.namespace.slice( 1 ) + 'tsscroller',
// Hide other scrollers so we can resize
$div = $( 'div.' + tscss.scrollerWrap + '[id!="' + id + '"]' )
.addClass( tscss.scrollerHideElement );
.addClass( tscss.scrollerHideElement ),
row = '<tr class="' + tscss.scrollerSpacerRow + ' ' + c.selectorRemove.slice(1) + '">';
wo.scroller_calcWidths = [];
// Remove fixed so we get proper widths and heights
tsScroller.removeFixed( c, wo );
$container.find( '.' + tscss.scrollerSpacerRow ).remove();
// remove ts added colgroups
$container.find( '.' + ts.css.colgroup ).remove();
// show original table elements to get proper alignment
$table
.find( '.' + tscss.scrollerHideElement )
.removeClass( tscss.scrollerHideElement );
// Reset sizes so parent can resize.
$table
.addClass( tscss.scrollerReset )
.children( 'thead' )
.find( '.' + tscss.headerIn )
.addClass( tscss.scrollerReset );
$tableWrap.addClass( tscss.scrollerReset );
// include left & right border widths
borderWidth = parseInt( $table.css( 'border-left-width' ), 10 );
$hCells = $hdr
.children( 'thead' )
.children( 'tr' )
.not( '.' + c.cssIgnoreRow )
.children( 'th, td' )
.filter( ':visible' );
$bCells = c.$tbodies
.eq( 0 )
.children( 'tr' )
.not( '.' + c.cssChildRow )
.eq( 0 )
.children( 'th, td' )
.filter( ':visible' );
$fCells = $foot
.children( 'tfoot' )
.children( 'tr' )
.children( 'th, td' )
.filter( ':visible' );
tsScroller.setWidth( $hCells.add( $bCells ).add( $fCells ), '' );
$headers = c.$headerIndexed;
for ( index = 0; index < $headers.length; index++ ) {
for ( index = 0; index < c.columns; index++ ) {
$this = $headers[ index ];
// code from https://github.com/jmosbech/StickyTableHeaders
if ( $this.css( 'box-sizing' ) === 'border-box' ) {
@ -406,13 +394,26 @@ ts.scroller = {
setWidth = $this.width();
}
}
temp = $hCells.eq( index )
.add( $bCells.eq( index ) )
.add( $fCells.eq( index ) );
tsScroller.setWidth( temp, setWidth );
row += '<td data-column="' + index + '" style="padding:0;margin:0;border:0;height:0;max-height:0;' +
'min-height:0;width:' + setWidth + 'px;min-width:' + setWidth + 'px;max-width:' + setWidth + 'px"></td>';
// save current widths
wo.scroller_calcWidths[ index ] = setWidth;
}
row += '</tr>';
c.$tbodies.eq(0).prepend( row ); // tbody
$hdr.children( 'thead' ).append( row );
$foot.children( 'tfoot' ).append( row );
// update resizable widget handles
c.$table.trigger( 'resizableUpdate' );
// include colgroup or alignment is off
if ( c.widthFixed ) {
ts.fixColumnWidth( c.table );
row = c.$table.children( 'colgroup' )[0].outerHTML;
$hdr.prepend( row );
$foot.prepend( row );
}
temp = $tableWrap.parent().innerWidth() -
( tsScroller.hasScrollBar( $tableWrap ) ? wo.scroller_barSetWidth : 0 );
@ -429,10 +430,6 @@ ts.scroller = {
$tableWrap
.width( setWidth + temp );
wo.scroller_$container
.find( '.' + tscss.scrollerReset )
.removeClass( tscss.scrollerReset );
// hide original table thead
$table.children( 'thead' ).addClass( tscss.scrollerHideElement );
@ -541,24 +538,16 @@ ts.scroller = {
// update thead & tbody in fixed column
var tsScroller = ts.scroller,
namespace = c.namespace + 'tsscrollerFixed',
// bind to ts-init or filterEnd, but not both!
events = ( ( ts.hasWidget( c.table, 'filter' ) ? 'filterEnd' : 'tablesorter-initialized' ) +
' sortEnd ' ).split( ' ' ).join( namespace + ' ' ),
events2 = 'scroll' + namespace,
events = 'scroll' + namespace,
$fixedTbody = wo.scroller_$fixedColumns.find( '.' + tscss.scrollerTable ),
fixedScroll = true,
tableScroll = true;
c.$table
.off( events )
.on( events, function() {
tsScroller.updateFixed( c, wo, false );
tsScroller.resize( c, wo );
})
.parent()
// *** SCROLL *** scroll fixed column along with main
.off( events2 )
.on( events2, function() {
.off( events )
.on( events, function() {
if ( wo.scroller_isBusy ) { return; }
// using flags to prevent firing the scroll event excessively leading to slow scrolling in Firefox
if ( fixedScroll || !tsScroller.isFirefox ) {
@ -571,8 +560,8 @@ ts.scroller = {
});
// scroll main along with fixed column
$fixedTbody
.off( events2 )
.on( events2, function() {
.off( events )
.on( events, function() {
// using flags to prevent firing the scroll event excessively leading to slow scrolling in Firefox
if ( tableScroll || !tsScroller.isFirefox ) {
fixedScroll = false;
@ -616,12 +605,39 @@ ts.scroller = {
}
},
updateFixed : function( c, wo ) {
adjustWidth : function( c, wo, totalWidth, adj, dir ) {
var $wrapper = wo.scroller_$container;
// RTL support (fixes column on right)
$wrapper
.children( '.' + tscss.scrollerTable )
.css( dir ? 'right' : 'left', totalWidth );
$wrapper
.children( '.' + tscss.scrollerHeader + ', .' + tscss.scrollerFooter )
// Safari needs a scrollbar width of extra adjusment to align the fixed & scrolling columns
.css( dir ? 'right' : 'left', totalWidth + ( dir && ts.scroller.isSafari ? adj : 0 ) );
},
updateFixed : function( c, wo ) {
var temp, adj,
$wrapper = wo.scroller_$container,
$hdr = wo.scroller_$header,
$foot = wo.scroller_$footer,
$table = c.$table,
$tableWrap = $table.parent(),
scrollBarWidth = wo.scroller_barSetWidth,
dir = $table.hasClass( tscss.scrollerRtl );
if ( wo.scroller_fixedColumns === 0 ) {
wo.scroller_isBusy = false;
ts.scroller.removeFixed( c, wo );
temp = $wrapper.width();
$tableWrap.width( temp );
adj = ts.scroller.hasScrollBar( $tableWrap ) ? scrollBarWidth : 0;
$hdr
.parent()
.add( $foot.parent() )
.width( temp - adj );
return;
}
@ -638,11 +654,7 @@ ts.scroller = {
// scroller_fixedColumns
var index, tbodyIndex, rowIndex, $tbody, $adjCol, $fb, $fixHead, $fixBody, $fixFoot,
totalRows, temp, adj, row,
$table = c.$table,
$tableWrap = $table.parent(),
$hdr = wo.scroller_$header,
$foot = wo.scroller_$footer,
totalRows, row,
// source cells for measurement
$mainTbodies = wo.scroller_$container
@ -666,9 +678,7 @@ ts.scroller = {
.children( 'tbody' ),
// variables
tsScroller = ts.scroller,
scrollBarWidth = wo.scroller_barSetWidth,
fixedColumns = wo.scroller_fixedColumns,
dir = $table.hasClass( tscss.scrollerRtl ),
// get dimensions
$temp = $table.find( 'tbody td' ),
borderRightWidth = parseInt( $temp.css( 'border-right-width' ), 10 ) || 1,
@ -690,15 +700,6 @@ ts.scroller = {
tsScroller.setWidth( $fixedColumn.add( $fixedColumn.children() ), totalWidth );
tsScroller.setWidth( $fixedColumn.children().children( 'table' ), totalWidth );
$table.find( '.' + tscss.scrollerSpacerRow ).remove();
row = '<tr class="' + tscss.scrollerSpacerRow + ' ' + c.selectorRemove.slice(1) + '">';
for ( index = 0; index < c.columns; index++ ) {
row += '<td style="padding:0; margin:0;height:0;max-height:0;min-height:0;width:' +
widths[ index ] + 'px;min-width:' + widths[ index ] + 'px;max-width:' +
widths[ index ] + 'px"></td>';
}
c.$tbodies.eq(0).prepend( row += '</tr>' );
// update fixed column tbody content, set cell widths on hidden row
for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) {
$tbody = $mainTbodies.eq( tbodyIndex );
@ -717,33 +718,6 @@ ts.scroller = {
.remove();
$fb.append( $adjCol );
}
// adjust fixed thead/tbody/tfoot cell widths
$fixHead = $fixedColumn
.find( 'thead' )
.children( 'tr.' + tscss.headerRow )
.children();
$fixBody = $fixedColumn
.find( tscss.scrollerSpacerRow )
.children();
$fixFoot = $fixedColumn
.find( 'tfoot' )
.children( 'tr' )
.eq( 0 )
.children();
// reusing variables, so ignore the names :P
$adjCol = $hdr.children( 'thead' ).children( 'tr' ).children( 'td, th' );
$rows = $foot.children( 'tfoot' ).children( 'tr' ).children( 'td, th' );
for ( index = 0; index < c.columns; index++ ) {
if ( index < fixedColumns ) {
$temp = $fixHead.eq( index )
.add( $fixBody.eq( index ) )
.add( $fixFoot.eq( index ) );
tsScroller.setWidth( $temp, widths[ index ] );
}
$temp = $adjCol.eq( index )
.add( $rows.eq( index ) );
tsScroller.setWidth( $temp, widths[ index ] );
}
// restore tbody
ts.processTbody( $fixedTbodiesTable, $fb, false );