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 */ });
|
.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();
|
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>
|
</script>
|
||||||
|
|||||||
@ -217,6 +217,15 @@ async function refresh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@ -282,6 +282,15 @@ async function loadStand() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@ -665,8 +665,12 @@ async function showZeitreihe(bundesland, partei) {
|
|||||||
'</tbody></table>';
|
'</tbody></table>';
|
||||||
|
|
||||||
if (window.Chart) {
|
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');
|
const ctx = document.getElementById('zeitreihe-chart');
|
||||||
new Chart(ctx, {
|
window._zeitreiheModalChart = new Chart(ctx, {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
data: {
|
data: {
|
||||||
labels: z.wahlperioden.map(r => 'WP ' + r.wp),
|
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) {
|
function closeModal(ev) {
|
||||||
if (!ev || ev.target.id === 'modal-backdrop') {
|
if (!ev || ev.target.id === 'modal-backdrop') {
|
||||||
document.getElementById('modal-backdrop').classList.remove('show');
|
document.getElementById('modal-backdrop').classList.remove('show');
|
||||||
|
_destroyModalChart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
document.addEventListener('keydown', (e) => {
|
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 ────────────────────────────────────────────
|
// ─── Stimmverhalten × Gemeinwohl ────────────────────────────────────────────
|
||||||
|
|||||||
@ -267,7 +267,21 @@ function showCluster(idx) {
|
|||||||
renderClusterGraph(cl);
|
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) {
|
function renderClusterGraph(cl) {
|
||||||
|
_stopForceSim();
|
||||||
const container = document.getElementById('cluster-graph');
|
const container = document.getElementById('cluster-graph');
|
||||||
if (!container || typeof d3 === 'undefined') return;
|
if (!container || typeof d3 === 'undefined') return;
|
||||||
const rawNodes = cl.nodes || [];
|
const rawNodes = cl.nodes || [];
|
||||||
@ -309,7 +323,7 @@ function renderClusterGraph(cl) {
|
|||||||
.attr('width', width).attr('height', height)
|
.attr('width', width).attr('height', height)
|
||||||
.attr('viewBox', `0 0 ${width} ${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)
|
.force('link', d3.forceLink(links).id(d => d.id)
|
||||||
.distance(l => (1 - (l.sim || 0)) * 220 + 50)
|
.distance(l => (1 - (l.sim || 0)) * 220 + 50)
|
||||||
.strength(l => l.sim || 0.5))
|
.strength(l => l.sim || 0.5))
|
||||||
@ -358,6 +372,7 @@ function renderClusterGraph(cl) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function showList() {
|
function showList() {
|
||||||
|
_stopForceSim();
|
||||||
document.getElementById('cluster-list').style.display = '';
|
document.getElementById('cluster-list').style.display = '';
|
||||||
document.getElementById('cluster-detail').style.display = 'none';
|
document.getElementById('cluster-detail').style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user