|
143 | 143 | window.crud.addBulkActionMainCheckboxesFunctionality = function(tableId = 'crudTable') { |
144 | 144 | const tableConfig = window.crud.tableConfigs[tableId] || window.crud; |
145 | 145 |
|
146 | | - // Update initial state for all main checkboxes |
| 146 | + // Find the wrapper that contains both the original table AND any cloned headers |
| 147 | + // (DataTables creates clones inside the wrapper for scrollX / fixedHeader) |
| 148 | + const wrapper = document.querySelector(`#${tableId}_wrapper`) || document.querySelector(`#${tableId}`)?.parentElement; |
| 149 | + if (!wrapper) return; |
| 150 | +
|
| 151 | + // Helper: update checked state on ALL general checkboxes within the wrapper |
147 | 152 | const updateMainCheckboxState = () => { |
148 | | - const rowCheckboxes = document.querySelectorAll(`#${tableId} input.crud_bulk_actions_line_checkbox`); |
149 | | - const uncheckedCount = document.querySelectorAll(`#${tableId} input.crud_bulk_actions_line_checkbox:not(:checked)`).length; |
150 | | - const shouldBeChecked = rowCheckboxes.length > 0 && uncheckedCount === 0; |
151 | | -
|
152 | | - // Update ALL main checkboxes (including cloned ones from DataTables fixedHeader) |
153 | | - document.querySelectorAll(`input.crud_bulk_actions_general_checkbox`).forEach(cb => { |
154 | | - const wrapper = cb.closest(`#${tableId}_wrapper`) || cb.closest(`#${tableId}`); |
155 | | - if (wrapper || document.querySelector(`#${tableId}`)) { |
| 153 | + const rowCheckboxes = document.querySelectorAll(`#${tableId} input.crud_bulk_actions_line_checkbox`); |
| 154 | + const uncheckedCount = document.querySelectorAll(`#${tableId} input.crud_bulk_actions_line_checkbox:not(:checked)`).length; |
| 155 | + const shouldBeChecked = rowCheckboxes.length > 0 && uncheckedCount === 0; |
| 156 | +
|
| 157 | + wrapper.querySelectorAll(`input.crud_bulk_actions_general_checkbox`).forEach(cb => { |
156 | 158 | cb.checked = shouldBeChecked; |
157 | | - } |
158 | | - }); |
| 159 | + }); |
159 | 160 | }; |
160 | 161 |
|
161 | | - // Initial state |
| 162 | + // Set initial state |
162 | 163 | updateMainCheckboxState(); |
163 | 164 |
|
164 | | - // Use event delegation on the table wrapper to handle clicks on ANY general checkbox |
165 | | - // This works even when DataTables clones/replaces elements (fixedHeader, scroll) |
166 | | - const wrapper = document.querySelector(`#${tableId}_wrapper`) || document.querySelector(`#${tableId}`)?.closest('.table-responsive') || document.body; |
167 | | -
|
168 | | - // Only attach delegation once per wrapper |
169 | | - if (!wrapper._bulkDelegationAttached) { |
170 | | - wrapper._bulkDelegationAttached = true; |
| 165 | + // Attach direct click handlers to ALL general checkboxes in the wrapper |
| 166 | + // (including clones created by DataTables for scrollX / fixedHeader). |
| 167 | + // cloneNode removes previous handlers so we never stack duplicates – the |
| 168 | + // same pattern used for line checkboxes in addOrRemoveCrudCheckedItem. |
| 169 | + wrapper.querySelectorAll('input.crud_bulk_actions_general_checkbox').forEach(checkbox => { |
| 170 | + const newCheckbox = checkbox.cloneNode(true); |
| 171 | + checkbox.parentNode.replaceChild(newCheckbox, checkbox); |
171 | 172 |
|
172 | | - wrapper.addEventListener('click', function(event) { |
173 | | - const checkbox = event.target; |
| 173 | + newCheckbox.addEventListener('click', function(e) { |
| 174 | + // Stop propagation immediately so the click never reaches the <th>, |
| 175 | + // which would trigger a DataTables column sort / table redraw. |
| 176 | + e.stopPropagation(); |
174 | 177 |
|
175 | | - // Check if clicked element is a general checkbox |
176 | | - if (!checkbox.classList.contains('crud_bulk_actions_general_checkbox')) { |
177 | | - return; |
178 | | - } |
| 178 | + const isChecked = this.checked; |
179 | 179 |
|
180 | | - const isChecked = checkbox.checked; |
181 | | -
|
182 | | - // Get all row checkboxes |
183 | | - const rowCheckboxes = Array.from(document.querySelectorAll(`#${tableId} input.crud_bulk_actions_line_checkbox`)); |
184 | | -
|
185 | | - // Toggle checkboxes that need to change |
186 | | - rowCheckboxes |
| 180 | + // Toggle row checkboxes that need to change |
| 181 | + Array.from(document.querySelectorAll(`#${tableId} input.crud_bulk_actions_line_checkbox`)) |
187 | 182 | .filter(elem => isChecked !== elem.checked) |
188 | 183 | .forEach(elem => elem.click()); |
189 | 184 |
|
190 | | - // Sync all main checkboxes (including clones) |
191 | | - document.querySelectorAll(`input.crud_bulk_actions_general_checkbox`).forEach(cb => { |
| 185 | + // Sync all main checkboxes (including clones) scoped to this table |
| 186 | + wrapper.querySelectorAll(`input.crud_bulk_actions_general_checkbox`).forEach(cb => { |
192 | 187 | cb.checked = isChecked; |
193 | | - }); |
| 188 | + }); |
194 | 189 |
|
195 | 190 | // Update bulk buttons |
196 | 191 | window.crud.enableOrDisableBulkButtons(tableId); |
197 | | -
|
198 | | - event.stopPropagation(); |
| 192 | + }); |
199 | 193 | }); |
200 | | - } |
201 | 194 | }; |
202 | 195 |
|
203 | 196 | if (typeof window.crud.enableOrDisableBulkButtons !== 'function') { |
|
0 commit comments