DataTables crashes if the table element is empty or removed from the DOM.
DataTables crashes if the table element is empty or removed from the DOM.
So I have a modal window element that contains a table that was initialized into a DataTable. If I close that modal window (jQuery empty() or/and remove()) and somehow re-open that modal window (is a new copy/instance element really), then we get this JavaScript crash:
Uncaught TypeError TypeError: Cannot read properties of undefined (reading 'style')
at _fnCalculateColumnWidths (datatables.js:17328:20)
at _fnAdjustColumnSizing (datatables.js:14021:4)
at <anonymous> (datatables.js:17447:6)
at <anonymous> (datatables.js:13273:9)
at dispatch (cdnjs.cloudflare.com/ajax/libs/jquery/3.6.2/jquery.min.js:2:43331)
at y.handle (cdnjs.cloudflare.com/ajax/libs/jquery/3.6.2/jquery.min.js:2:41315)
Now I have to inject a code inside my generic function to handle that crash. I'd rather not have to deal with checking if one of my descendant elements is a DataTable and destroy it before the modal window is closed and disposed off.
DataTables should be able to check if it still exists and destroy itself or/and suppress those errors when it is trying to read/write on an undefined object.
Answers
I'm not sure what your process to close the modal is since you didn't post any code. Datatables won't check to see if it still exists. However if closing the modal removes the table that Datatables is initialized against then you will need to use
destroy()
before removing the table. You can useDataTable.isDataTable()
to check if the table element is a Datatable and handle appropriately.Kevin
Yes, I cannot post the code for the modal and all of that as it would be too much info. But I have tested it this morning to see how it crashes and it is definitely when I do a jQuery .empty() or .remove() on the table element that holds the DataTables.
I think the code line that tries to read/write the undefined "style" property of the element should do a check to make sure it is not undefined and that should silence the error. The better way would be to catch that error and do a quick check and see if the table element still exists and if not, auto-destroy the DataTable for the user.
If it catches the error and silences it then the developer won't know if there is a problem when there is a bug in the their code. The error occurs for more than just the table element missing.
I won't speak for @allan but I think the proper way is for the application developer to properly handle removing components added by the application and not expect/rely on the library to check for them. For example if there is a bug in the application that unexpectedly removes the table element then the developer wouldn't know if Datatables just destroys itself.
Just my humble opinions. @alan may see it differently.
Kevin
Perhaps you can use this example to modify it to show the issue you are seeing?
The correct thing to do would be to
destroy()
the DataTable before removing the modal. Indeed if you don't do that you most certainly have a memory leak. DataTables does not monitor the DOM so it doesn't know if it has been removed.I think it would be wrong for DataTables to check if it has been removed on each draw and automatically destroy itself if so. It is perfectly valid to reinsert an existing DataTable.
I think the onus is on the developer (yourself ) to destroy the table if you don't want to use it again.
Allan
I will say that the developer would have no idea that removing a table element without first destroying the DataTable is a mistake. That would be an internal error handling the DataTable is aware of and is the only entity responsible to handle/catch that error. You would think that manipulating the table element is higher in hierarchy then manipulating the DataTable. That means that whatever happens to the table element, the DataTable needs to adapt to the change and not scream out bloody murder because we did first something we should not have.
I'm not exactly sure what the DataTable was trying to do, but it's obviously event-based as the error happened the moment I re-opened the modal window.
The lazy coder will tweak the code as follow to silence that specific error:
But then I see we get another similar error on line 17420.
The lazy coder will do the same thing there:
And guess what... No more errors.
Really now, the DataTables should handle that error and be self-ware Skynet-style and auto-destroy itself if its existence is no longer relevant to perhaps free up some of the memory it is using.
Not related to my question here, but just an observation and perhaps enhancement suggestion. I have noticed that when we create our DataTable, the API does not **hide **the table element before it does its thing. I have noticed an improvement in speed when initializing a DataTable if the table element is hidden beforehand. In the test, I generated a table with 10000 rows and 50 columns with random numbers. There is no setTimeout or anything like that and the table is created, added to the DOM and the DataTable() call is done.
Here are the results:
Current default DataTables initialization: 45 seconds + potential browser out of memory crash because of the large table element still rendered.
Hiding the table element before the DataTables initialization: 32 seconds
I'm pretty sure the graph gets worse for the current default initialization the more table rows we have. Either way, the original table element is hidden once the DataTables initializes and takes over the table element.
This FAQ explains options to speed up Datatables loading.
Kevin
32 seconds is terribly slow to render 10k rows and 50 columns. I'd be interested in profiling that page is you can give me a link to it.
You most certainly want paging and
deferRender
with Ajax loaded data if you are loading that amount of data. No need to create all those DOM elements up front.Regarding the destroy, perhaps in future I'll implement an auto detroy option, but at the moment I'm happy to assume that developers will free up resources (release event handlers and the like) if they no longer need a component.
Allan
You don't even need to access my page, although it is on the intranet. You simply need to generate a table with 10000 rows and 50 columns on a blank page. Put in each cell any numbers. I do have alternating row background coloring with my row CSS, but that should be ok... Apply DataTable() and if you are getting faster speed, say 1-5 seconds... please let me know? I shall look into this further tomorrow. Cheers!
I will see about not creating the table upfront..but for now, they will be created upfront
See this example:
https://live.datatables.net/catequfi/1
It generates an HTML table with 50 columns and 10000 rows. It then inits a default Datatable. It destroys the Datatable then reinitializes with
orderClasses
set false. Example output:I ran it a few times and the time to initialize the default Datatable was always under 8 seconds. Looks like setting
orderClasses
false drops the init time ~2 seconds.Do you have complex rendering in your table?
Do you use options like
columns.render
?The first step I would take is to determine how much of the 45 seconds is taken by the HTML table build versus the time it takes to initialize Datatables. I think you will benefit by using
ajax
loaded data anddeferRender
. Worst case is using Server Side Processing to page the rows sent to the client.Kevin
And with a data array and
deferRender
- 192mS initialisation and first draw time for me: https://live.datatables.net/catequfi/3/edit .There are so many options and variables (not just in DataTables, but internet connection, other resources on the page) that we would unequivocally need a link to the page showing the issue to be able to profile it properly.
Allan