While working on customising a page powered by DataTables 1.9.4 I stumbled upon an amusing bug in its logic. When you select a display length (indicated as Show # entities by default), DataTables will actually edit ALL select entities in the document to the value you selected. In my instance, I have five other select HTML entities, one of which has over 100 elements whose values are numeric. When setting Display Length from 500 to 100, I noticed my combo box would suddenly jump to the item that corresponds to value 100.
In addition to this report I am submitting a fix to this problem. The correction I devised revises the jQuery statements to uniquely select the Display Length select element only, and not all of the select elements in the document.
Original code from dataTables-1.9.4.js:
My revised version of the _fnFeatureHtmlLength function which makes use of the name of the select element being created:
It may be more elegant to just refine a variable to make the application of this adjustment more readable; this was a quick fix to solve the bug and easy to identify for this report.
In addition to this report I am submitting a fix to this problem. The correction I devised revises the jQuery statements to uniquely select the Display Length select element only, and not all of the select elements in the document.
Original code from dataTables-1.9.4.js:
/** * Generate the node required for user display length changing * @param {object} oSettings dataTables settings object * @returns {node} Display length feature node * @memberof DataTable#oApi */ function _fnFeatureHtmlLength ( oSettings ) { if ( oSettings.oScroll.bInfinite ) { return null; } /* This can be overruled by not using the _MENU_ var/macro in the language variable */ var sName = 'name="'+oSettings.sTableId+'_length"'; var sStdMenu = '<select size="1" '+sName+'>'; var i, iLen; var aLengthMenu = oSettings.aLengthMenu; if ( aLengthMenu.length == 2 && typeof aLengthMenu[0] === 'object' && typeof aLengthMenu[1] === 'object' ) { for ( i=0, iLen=aLengthMenu[0].length ; i<iLen ; i++ ) { sStdMenu += '<option value="'+aLengthMenu[0][i]+'">'+aLengthMenu[1][i]+'</option>'; } } else { for ( i=0, iLen=aLengthMenu.length ; i<iLen ; i++ ) { sStdMenu += '<option value="'+aLengthMenu[i]+'">'+aLengthMenu[i]+'</option>'; } } sStdMenu += '</select>'; var nLength = document.createElement( 'div' ); if ( !oSettings.aanFeatures.l ) { nLength.id = oSettings.sTableId+'_length'; } nLength.className = oSettings.oClasses.sLength; nLength.innerHTML = '<label>'+oSettings.oLanguage.sLengthMenu.replace( '_MENU_', sStdMenu )+'</label>'; /* * Set the length to the current display length - thanks to Andrea Pavlovic for this fix, * and Stefan Skopnik for fixing the fix! */ $('select option[value="'+oSettings._iDisplayLength+'"]', nLength).attr("selected", true); $('select', nLength).bind( 'change.DT', function(e) { var iVal = $(this).val(); /* Update all other length options for the new display */ var n = oSettings.aanFeatures.l; for ( i=0, iLen=n.length ; i<iLen ; i++ ) { if ( n[i] != this.parentNode ) { $('select', n[i]).val( iVal ); } } /* Redraw the table */ oSettings._iDisplayLength = parseInt(iVal, 10); _fnCalculateEnd( oSettings ); /* If we have space to show extra rows (backing up from the end point - then do so */ if ( oSettings.fnDisplayEnd() == oSettings.fnRecordsDisplay() ) { oSettings._iDisplayStart = oSettings.fnDisplayEnd() - oSettings._iDisplayLength; if ( oSettings._iDisplayStart < 0 ) { oSettings._iDisplayStart = 0; } } if ( oSettings._iDisplayLength == -1 ) { oSettings._iDisplayStart = 0; } _fnDraw( oSettings ); } ); $('select', nLength).attr('aria-controls', oSettings.sTableId); return nLength; }
My revised version of the _fnFeatureHtmlLength function which makes use of the name of the select element being created:
/** * Generate the node required for user display length changing * @param {object} oSettings dataTables settings object * @returns {node} Display length feature node * @memberof DataTable#oApi */ function _fnFeatureHtmlLength ( oSettings ) { if ( oSettings.oScroll.bInfinite ) { return null; } /* This can be overruled by not using the _MENU_ var/macro in the language variable */ var sName = 'name="'+oSettings.sTableId+'_length"'; var sStdMenu = '<select size="1" '+sName+'>'; var i, iLen; var aLengthMenu = oSettings.aLengthMenu; if ( aLengthMenu.length == 2 && typeof aLengthMenu[0] === 'object' && typeof aLengthMenu[1] === 'object' ) { for ( i=0, iLen=aLengthMenu[0].length ; i<iLen ; i++ ) { sStdMenu += '<option value="'+aLengthMenu[0][i]+'">'+aLengthMenu[1][i]+'</option>'; } } else { for ( i=0, iLen=aLengthMenu.length ; i<iLen ; i++ ) { sStdMenu += '<option value="'+aLengthMenu[i]+'">'+aLengthMenu[i]+'</option>'; } } sStdMenu += '</select>'; var nLength = document.createElement( 'div' ); if ( !oSettings.aanFeatures.l ) { nLength.id = oSettings.sTableId+'_length'; } nLength.className = oSettings.oClasses.sLength; nLength.innerHTML = '<label>'+oSettings.oLanguage.sLengthMenu.replace( '_MENU_', sStdMenu )+'</label>'; /* * Set the length to the current display length - thanks to Andrea Pavlovic for this fix, * and Stefan Skopnik for fixing the fix! */ $('select[name='+oSettings.sTableId+'_length] option[value="'+oSettings._iDisplayLength+'"]', nLength).attr("selected", true); $('select[name='+oSettings.sTableId+'_length]', nLength).bind( 'change.DT', function(e) { var iVal = $(this).val(); /* Update all other length options for the new display */ var n = oSettings.aanFeatures.l; for ( i=0, iLen=n.length ; i<iLen ; i++ ) { if ( n[i] != this.parentNode ) { $('select[name='+oSettings.sTableId+'_length]', n[i]).val( iVal ); } } /* Redraw the table */ oSettings._iDisplayLength = parseInt(iVal, 10); _fnCalculateEnd( oSettings ); /* If we have space to show extra rows (backing up from the end point - then do so */ if ( oSettings.fnDisplayEnd() == oSettings.fnRecordsDisplay() ) { oSettings._iDisplayStart = oSettings.fnDisplayEnd() - oSettings._iDisplayLength; if ( oSettings._iDisplayStart < 0 ) { oSettings._iDisplayStart = 0; } } if ( oSettings._iDisplayLength == -1 ) { oSettings._iDisplayStart = 0; } _fnDraw( oSettings ); } ); $('select[name='+oSettings.sTableId+'_length]', nLength).attr('aria-controls', oSettings.sTableId); return nLength; }
It may be more elegant to just refine a variable to make the application of this adjustment more readable; this was a quick fix to solve the bug and easy to identify for this report.