From 3c622d7dd8c3a64904704b6faf78d9336d255c2c Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 6 Sep 2026 15:11:59 +0200 Subject: [PATCH] Destroy DataTables before Turbo caches the page The back button restored a Turbo snapshot that still held DataTables' injected search / length controls; re-init on turbo:load then stacked another set each time. Tear every instance down on turbo:before-cache so the cached snapshot is a plain table again. Co-Authored-By: Claude Sonnet 5 --- assets/app.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/assets/app.js b/assets/app.js index ea38c1f..ca0f83b 100644 --- a/assets/app.js +++ b/assets/app.js @@ -48,9 +48,15 @@ function columnFromTh(th, rowUrl) { return col; } +// Live DataTable instances, keyed by their element. Used to tear them +// down before Turbo caches a page - otherwise the back button restores a +// snapshot that still contains DataTables' injected controls, and re-init +// stacks a second set of "search" / "entries per page" rows on top. +const instances = new Map(); + function initDataTables(root = document) { root.querySelectorAll('table.datatable').forEach((table) => { - if (table.dataset.datatable === 'false' || DataTable.isDataTable(table)) { + if (table.dataset.datatable === 'false' || instances.has(table) || DataTable.isDataTable(table)) { return; } @@ -76,9 +82,16 @@ function initDataTables(root = document) { }; } - new DataTable(table, options); + instances.set(table, new DataTable(table, options)); }); } +function destroyDataTables() { + instances.forEach((dt) => dt.destroy()); + instances.clear(); +} + document.addEventListener('DOMContentLoaded', () => initDataTables()); document.addEventListener('turbo:load', () => initDataTables()); +// Restore the plain
before Turbo snapshots the page for its cache. +document.addEventListener('turbo:before-cache', destroyDataTables);