From bda882d89a63b465074ff8f155fd1a85444bd05d Mon Sep 17 00:00:00 2001 From: Anton Volnuhin Date: Sun, 8 Feb 2026 01:01:00 +0300 Subject: [PATCH] Fix eye button tap on touch devices in details panel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On touch devices, tapping an eye button in the details panel would trigger chart mouseout → showDefaultView() which rebuilt the DOM before the click event fired. Added pointerdown/pointerup guards on the details box to prevent the race condition. Also added @media (pointer: coarse) to always show eye buttons on touch devices regardless of viewport width. Co-Authored-By: Claude Opus 4.6 --- app.js | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/app.js b/app.js index c257430..15cea26 100644 --- a/app.js +++ b/app.js @@ -3243,7 +3243,20 @@ function setupHoverEvents(sunburstData, contextName = null) { // Track if we're inside a section to handle sector exit properly let isInsideSection = false; - + + // Prevent details panel reset when user is interacting with it (touch devices) + let isInteractingWithDetails = false; + const detailsBox = document.getElementById('details-box'); + if (detailsBox) { + detailsBox.addEventListener('pointerdown', () => { + isInteractingWithDetails = true; + }); + // Clear after a short delay to allow click handlers to fire + detailsBox.addEventListener('pointerup', () => { + setTimeout(() => { isInteractingWithDetails = false; }, 300); + }); + } + // Add general mousemove event listener to detect when outside chart circle chartDom.addEventListener('mousemove', function(e) { // Get mouse position relative to chart container @@ -3256,7 +3269,7 @@ function setupHoverEvents(sunburstData, contextName = null) { lastMouseY = mouseY; // If not inside the chart circle and we were inside a section, show default view - if (!isInsideChart(mouseX, mouseY) && isInsideSection) { + if (!isInsideChart(mouseX, mouseY) && isInsideSection && !isInteractingWithDetails) { isInsideSection = false; // Reset details immediately when leaving a section showDefaultView(); @@ -3403,7 +3416,7 @@ function setupHoverEvents(sunburstData, contextName = null) { hideChartEyeButton(); // Reset details with a small delay to allow mouseover of next sector to fire first setTimeout(() => { - if (!isOverEyeButton && !isInsideSection) { + if (!isOverEyeButton && !isInsideSection && !isInteractingWithDetails) { showDefaultView(); } }, 50); @@ -3416,6 +3429,7 @@ function setupHoverEvents(sunburstData, contextName = null) { if (e.relatedTarget === chartEyeBtn || (e.relatedTarget && chartEyeBtn.contains(e.relatedTarget))) { return; } + if (isInteractingWithDetails) return; isInsideSection = false; showDefaultView(); }); @@ -3424,7 +3438,7 @@ function setupHoverEvents(sunburstData, contextName = null) { myChart.on('downplay', function(params) { // Reset to default view when a section is no longer emphasized (unless hovering eye button or still in section) setTimeout(() => { - if (!isOverEyeButton && !isInsideSection) { + if (!isOverEyeButton && !isInsideSection && !isInteractingWithDetails) { showDefaultView(); } }, 50);