fix(#183): Browser-Speicher — Modal-Chart-destroy, Force-Sim-stop, Polling-Pause
- **Zeitreihe-Modal-Chart**: vor Re-Render alte Chart.destroy(), bei closeModal() + Escape ebenfalls. Vorher akkumulierten sich Chart-Instanzen bei jedem Modal-Open (Listener bleiben hängen). - **Cluster-Force-Sim**: `_currentForceSim` global gemerkt, beim Verlassen Detail-View (showList) und vor neuem renderClusterGraph per sim.stop() beendet. Vorher liefen tick-Listener weiter, hielten nodes/links/SVG-Refs. - **Polling-Pause auf visibilitychange** in queue_widget, admin_stand und admin_queue. Wenn Tab versteckt → clearInterval, beim Show wieder starten. Spart CPU/Akku + verhindert verwaiste Polls. Refs: #183. Heap-Snapshot-Verifikation in Folge-Schritt. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d8999f8a64
commit
481a791934
@ -86,8 +86,17 @@
|
||||
})
|
||||
.catch(function () { /* still */ });
|
||||
}
|
||||
// erster Aufruf direkt + danach alle 5 s
|
||||
// Polling: erster Aufruf + alle 5 s. Bei Tab-Wechsel/Hide pausieren,
|
||||
// damit der Browser CPU/Akku spart und keine Listener akkumuliert (#183).
|
||||
poll();
|
||||
setInterval(poll, 5000);
|
||||
let _qwInterval = setInterval(poll, 5000);
|
||||
document.addEventListener('visibilitychange', function () {
|
||||
if (document.hidden) {
|
||||
if (_qwInterval) { clearInterval(_qwInterval); _qwInterval = null; }
|
||||
} else if (!_qwInterval) {
|
||||
poll();
|
||||
_qwInterval = setInterval(poll, 5000);
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
@ -217,6 +217,15 @@ async function refresh() {
|
||||
}
|
||||
|
||||
refresh();
|
||||
setInterval(refresh, 5000);
|
||||
let _queueInterval = setInterval(refresh, 5000);
|
||||
// Pause-Polling wenn Tab versteckt (#183).
|
||||
document.addEventListener('visibilitychange', function () {
|
||||
if (document.hidden) {
|
||||
if (_queueInterval) { clearInterval(_queueInterval); _queueInterval = null; }
|
||||
} else if (!_queueInterval) {
|
||||
refresh();
|
||||
_queueInterval = setInterval(refresh, 5000);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@ -282,6 +282,15 @@ async function loadStand() {
|
||||
}
|
||||
|
||||
loadStand();
|
||||
setInterval(loadStand, 30000);
|
||||
let _standInterval = setInterval(loadStand, 30000);
|
||||
// Pause-Polling wenn Tab versteckt (#183).
|
||||
document.addEventListener('visibilitychange', function () {
|
||||
if (document.hidden) {
|
||||
if (_standInterval) { clearInterval(_standInterval); _standInterval = null; }
|
||||
} else if (!_standInterval) {
|
||||
loadStand();
|
||||
_standInterval = setInterval(loadStand, 30000);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@ -665,8 +665,12 @@ async function showZeitreihe(bundesland, partei) {
|
||||
'</tbody></table>';
|
||||
|
||||
if (window.Chart) {
|
||||
// Modal-Chart-Tracking: vor Re-Render alte Instanz zerstören.
|
||||
if (window._zeitreiheModalChart) {
|
||||
try { window._zeitreiheModalChart.destroy(); } catch (_) {}
|
||||
}
|
||||
const ctx = document.getElementById('zeitreihe-chart');
|
||||
new Chart(ctx, {
|
||||
window._zeitreiheModalChart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: z.wahlperioden.map(r => 'WP ' + r.wp),
|
||||
@ -700,13 +704,23 @@ async function showZeitreihe(bundesland, partei) {
|
||||
}
|
||||
}
|
||||
|
||||
function _destroyModalChart() {
|
||||
if (window._zeitreiheModalChart) {
|
||||
try { window._zeitreiheModalChart.destroy(); } catch (_) {}
|
||||
window._zeitreiheModalChart = null;
|
||||
}
|
||||
}
|
||||
function closeModal(ev) {
|
||||
if (!ev || ev.target.id === 'modal-backdrop') {
|
||||
document.getElementById('modal-backdrop').classList.remove('show');
|
||||
_destroyModalChart();
|
||||
}
|
||||
}
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape') document.getElementById('modal-backdrop').classList.remove('show');
|
||||
if (e.key === 'Escape') {
|
||||
document.getElementById('modal-backdrop').classList.remove('show');
|
||||
_destroyModalChart();
|
||||
}
|
||||
});
|
||||
|
||||
// ─── Stimmverhalten × Gemeinwohl ────────────────────────────────────────────
|
||||
|
||||
@ -267,7 +267,21 @@ function showCluster(idx) {
|
||||
renderClusterGraph(cl);
|
||||
}
|
||||
|
||||
// Aktuelle Force-Simulation — beim Verlassen der Detail-View stoppen,
|
||||
// sonst läuft Tick-Listener weiter und akkumuliert Memory (#183).
|
||||
let _currentForceSim = null;
|
||||
|
||||
function _stopForceSim() {
|
||||
if (_currentForceSim) {
|
||||
try { _currentForceSim.stop(); } catch (_) {}
|
||||
_currentForceSim = null;
|
||||
}
|
||||
const container = document.getElementById('cluster-graph');
|
||||
if (container) container.innerHTML = '';
|
||||
}
|
||||
|
||||
function renderClusterGraph(cl) {
|
||||
_stopForceSim();
|
||||
const container = document.getElementById('cluster-graph');
|
||||
if (!container || typeof d3 === 'undefined') return;
|
||||
const rawNodes = cl.nodes || [];
|
||||
@ -309,7 +323,7 @@ function renderClusterGraph(cl) {
|
||||
.attr('width', width).attr('height', height)
|
||||
.attr('viewBox', `0 0 ${width} ${height}`);
|
||||
|
||||
const sim = d3.forceSimulation(nodes)
|
||||
const sim = (_currentForceSim = d3.forceSimulation(nodes))
|
||||
.force('link', d3.forceLink(links).id(d => d.id)
|
||||
.distance(l => (1 - (l.sim || 0)) * 220 + 50)
|
||||
.strength(l => l.sim || 0.5))
|
||||
@ -358,6 +372,7 @@ function renderClusterGraph(cl) {
|
||||
}
|
||||
|
||||
function showList() {
|
||||
_stopForceSim();
|
||||
document.getElementById('cluster-list').style.display = '';
|
||||
document.getElementById('cluster-detail').style.display = 'none';
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user