1
0
mirror of https://github.com/DataTables/DataTables.git synced 2025-02-19 17:54:14 +01:00

Fix: Memory leak with child rows in a table that is destroyed

New: `dt-api row().child.remove()` and `dt-api row().child().remove()` methods to provide the ability to remove and destroy child rows
Fix docs: Note that the row().child() method's return value can have an effect on the chaining

* This fixed DataTables/DataTables issue #326.
* Special thanks to `Scottmitch1` (on github) for help with this one!
This commit is contained in:
Allan Jardine 2014-06-18 12:23:50 +01:00
parent 097f45855f
commit 7bca91d1b5
3 changed files with 77 additions and 25 deletions

View File

@ -1 +1 @@
56e4fd1cdbaddc9b9ca4d4788c88640caa7db1e3
2cee9bcfddd46dfb0afc36325d550b8fb99f0f1d

View File

@ -71,7 +71,7 @@ $(document).ready(function() {
if ( row.child.isShown() ) {
// This row is already open - close it
row.child.hide();
row.child( false );
tr.removeClass('shown');
}
else {
@ -185,7 +185,7 @@ $(document).ready(function() {
if ( row.child.isShown() ) {
// This row is already open - close it
row.child.hide();
row.child( false );
tr.removeClass('shown');
}
else {

View File

@ -7717,26 +7717,42 @@
};
var __details_display = function ( show ) {
var ctx = this.context;
var __details_remove = function ( api )
{
var ctx = api.context;
if ( ctx.length && this.length ) {
var row = ctx[0].aoData[ this[0] ];
if ( ctx.length && api.length ) {
var row = ctx[0].aoData[ api[0] ];
if ( row._details ) {
row._details.remove();
row._detailsShow = undefined;
row._details = undefined;
}
}
};
var __details_display = function ( api, show ) {
var ctx = api.context;
if ( ctx.length && api.length ) {
var row = ctx[0].aoData[ api[0] ];
if ( row._details ) {
row._detailsShow = show;
if ( show ) {
row._details.insertAfter( row.nTr );
}
else {
row._details.remove();
row._details.detach();
}
__details_events( ctx[0] );
}
}
return this;
};
@ -7746,15 +7762,17 @@
var namespace = '.dt.DT_details';
var drawEvent = 'draw'+namespace;
var colvisEvent = 'column-visibility'+namespace;
var destroyEvent = 'destroy'+namespace;
var data = settings.aoData;
api.off( drawEvent +' '+ colvisEvent );
api.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent );
if ( _pluck( settings.aoData, '_details' ).length > 0 ) {
if ( _pluck( data, '_details' ).length > 0 ) {
// On each draw, insert the required elements into the document
api.on( drawEvent, function () {
api.rows( {page:'current'} ).eq(0).each( function (idx) {
// Internal data grab
var row = settings.aoData[ idx ];
var row = data[ idx ];
if ( row._detailsShow ) {
row._details.insertAfter( row.nTr );
@ -7768,22 +7786,36 @@
// a colspan)
var row, visible = _fnVisbleColumns( settings );
for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
row = settings.aoData[i];
for ( var i=0, ien=data.length ; i<ien ; i++ ) {
row = data[i];
if ( row._details ) {
row._details.children('td[colspan]').attr('colspan', visible );
}
}
} );
// Table destroyed - nuke any child rows
api.on( destroyEvent, function () {
for ( var i=0, ien=data.length ; i<ien ; i++ ) {
if ( data[i]._details ) {
__details_remove( data[i] );
}
}
} );
}
};
// Strings for the method names to help minification
var _emp = '';
var _child_obj = _emp+'row().child';
var _child_mth = _child_obj+'()';
// data can be:
// tr
// string
// jQuery or array of any of the above
_api_register( 'row().child()', function ( data, klass ) {
_api_register( _child_mth, function ( data, klass ) {
var ctx = this.context;
if ( data === undefined ) {
@ -7792,6 +7824,14 @@
ctx[0].aoData[ this[0] ]._details :
undefined;
}
else if ( data === true ) {
// show
this.child.show();
}
else if ( data === false ) {
// remove
__details_remove( this );
}
else if ( ctx.length && this.length ) {
// set
__details_add( ctx[0], ctx[0].aoData[ this[0] ], data, klass );
@ -7800,23 +7840,35 @@
return this;
} );
_api_register( [
'row().child.show()',
'row().child().show()'
], function () {
__details_display.call( this, true );
_child_obj+'.show()',
_child_mth+'.show()' // only when `child()` was called with parameters (without
], function ( show ) { // it returns an object and this method is not executed)
__details_display( this, true );
return this;
} );
_api_register( [
'row().child.hide()',
'row().child().hide()'
], function () {
__details_display.call( this, false );
_child_obj+'.hide()',
_child_mth+'.hide()' // only when `child()` was called with parameters (without
], function () { // it returns an object and this method is not executed)
__details_display( this, false );
return this;
} );
_api_register( 'row().child.isShown()', function () {
_api_register( [
_child_obj+'.remove()',
_child_mth+'.remove()' // only when `child()` was called with parameters (without
], function () { // it returns an object and this method is not executed)
__details_remove( this );
return this;
} );
_api_register( _child_obj+'.isShown()', function () {
var ctx = this.context;
if ( ctx.length && this.length ) {