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.

nya13nya13 Posts: 21Questions: 6Answers: 0

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

  • kthorngrenkthorngren Posts: 21,299Questions: 26Answers: 4,945
    edited November 2023

    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 use DataTable.isDataTable() to check if the table element is a Datatable and handle appropriately.

    Kevin

  • nya13nya13 Posts: 21Questions: 6Answers: 0

    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.

  • kthorngrenkthorngren Posts: 21,299Questions: 26Answers: 4,945
    edited November 2023

    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.

    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.

    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.

    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

  • allanallan Posts: 63,451Questions: 1Answers: 10,465 Site admin

    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

  • nya13nya13 Posts: 21Questions: 6Answers: 0

    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:

        if (headerCells[i])
            headerCells[i].style.width = column.sWidthOrig !== null && column.sWidthOrig !== '' ?
                _fnStringToCss( column.sWidthOrig ) :
                '';
    

    But then I see we get another similar error on line 17420.

    The lazy coder will do the same thing there:

    if (headerCells[i])
        var bounding = browser.bBounding ?
            Math.ceil( headerCells[i].getBoundingClientRect().width ) :
            cell.outerWidth();
    

    And guess what... No more errors. >:)

  • nya13nya13 Posts: 21Questions: 6Answers: 0

    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. ;)

  • nya13nya13 Posts: 21Questions: 6Answers: 0

    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. :)

  • kthorngrenkthorngren Posts: 21,299Questions: 26Answers: 4,945

    This FAQ explains options to speed up Datatables loading.

    Kevin

  • allanallan Posts: 63,451Questions: 1Answers: 10,465 Site admin

    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

  • nya13nya13 Posts: 21Questions: 6Answers: 0

    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? :p I shall look into this further tomorrow. Cheers!

  • nya13nya13 Posts: 21Questions: 6Answers: 0

    I will see about not creating the table upfront..but for now, they will be created upfront :D

  • kthorngrenkthorngren Posts: 21,299Questions: 26Answers: 4,945
    edited November 2023

    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:

    Initializing HTML table with 50 columns and 10000 rows, please wait.
    HTML table build complete in 1597 milliseconds.
    DT with orderClasses init complete in 6400 milliseconds.
    DT with orderClasses false init complete in 4449 milliseconds.
    

    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 and deferRender. Worst case is using Server Side Processing to page the rows sent to the client.

    Kevin

  • allanallan Posts: 63,451Questions: 1Answers: 10,465 Site admin

    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

Sign In or Register to comment.