fix(sensor): replace static NodeList while-loops causing page freeze and removeChild TypeError

Four list-trimming loops used querySelectorAll (static NodeList) inside a
while condition, so .length never decreased — causing infinite loops that
froze the page, or repeated removeChild calls on already-removed nodes
(TypeError: parameter 1 is not of type 'Node').

Also replaces blocking alert() with showInfo() for start errors and adds
a .catch() handler to the start_sensor fetch so network failures surface
cleanly instead of leaving the UI in a broken state.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
James Smith
2026-04-12 21:28:50 +01:00
parent 0210791c69
commit 1fc80b05b1
+12 -15
View File
@@ -5341,8 +5341,11 @@
// Clear existing output // Clear existing output
output.innerHTML = '<div class="placeholder signal-empty-state" style="display: none;"></div>'; output.innerHTML = '<div class="placeholder signal-empty-state" style="display: none;"></div>';
} else { } else {
alert('Error: ' + data.message); showInfo('Error: ' + (data.message || 'Failed to start sensor'));
} }
})
.catch(err => {
showInfo('Error starting sensor: ' + err.message);
}); });
} }
@@ -5580,8 +5583,8 @@
// Keep list manageable // Keep list manageable
const cards = output.querySelectorAll('.signal-card'); const cards = output.querySelectorAll('.signal-card');
while (cards.length > 100) { for (let i = cards.length - 1; i >= 100; i--) {
output.removeChild(output.lastChild); cards[i].remove();
} }
} }
@@ -5864,14 +5867,8 @@
// Limit to max 50 unique meters (cards won't pile up since we update in place) // Limit to max 50 unique meters (cards won't pile up since we update in place)
const cards = output.querySelectorAll('.signal-card.meter-aggregated'); const cards = output.querySelectorAll('.signal-card.meter-aggregated');
while (cards.length > 50) { for (let i = cards.length - 1; i >= 50; i--) {
// Remove oldest card (last one) cards[i].remove();
const oldestCard = output.querySelector('.signal-card.meter-aggregated:last-of-type');
if (oldestCard) {
output.removeChild(oldestCard);
} else {
break;
}
} }
} }
@@ -7055,8 +7052,8 @@
// Limit messages displayed (keep placeholder/empty-state) // Limit messages displayed (keep placeholder/empty-state)
const cards = output.querySelectorAll('.signal-card'); const cards = output.querySelectorAll('.signal-card');
while (cards.length > 100) { for (let i = cards.length - 1; i >= 100; i--) {
output.removeChild(cards[cards.length - 1]); cards[i].remove();
} }
} }
@@ -7420,7 +7417,7 @@
// Limit displayed devices // Limit displayed devices
while (content.children.length > 50) { while (content.children.length > 50) {
content.removeChild(content.lastChild); content.lastElementChild.remove();
} }
} }
@@ -10707,7 +10704,7 @@
// Keep log manageable // Keep log manageable
while (logEl.children.length > 100) { while (logEl.children.length > 100) {
logEl.removeChild(logEl.lastChild); logEl.lastElementChild.remove();
} }
// Update map if position data // Update map if position data