be67cde22f4d9af8323ef62560754b5ef925d020 chmalee Mon Mar 9 16:19:25 2026 -0700 Fix two regression in tooltip code from commit ddfed922f22d47: not clearing tooltips on mouse scroll and not clearing tooltips when showing a wiggle mouseover, refs #37205 diff --git src/hg/js/utils.js src/hg/js/utils.js index f2d07f87b39..ffd7a62f74d 100644 --- src/hg/js/utils.js +++ src/hg/js/utils.js @@ -4201,30 +4201,47 @@ function addMouseover(ele1, text = null, ele2 = null) { /* Adds wrapper elements to control various mouseover events using mouseenter/mouseleave. */ if (!mouseoverContainer) { mouseoverContainer = document.createElement("div"); mouseoverContainer.className = "tooltip"; mouseoverContainer.style.position = "fixed"; mouseoverContainer.style.display = "inline-block"; mouseoverContainer.style.visibility = "hidden"; mouseoverContainer.style.opacity = "0"; mouseoverContainer.id = "mouseoverContainer"; let tooltipTextSize = localStorage.getItem("tooltipTextSize"); if (tooltipTextSize === null) {tooltipTextSize = window.browserTextSize !== null ? window.browserTextSize : 12;} mouseoverContainer.style.fontSize = tooltipTextSize + "px"; document.body.append(mouseoverContainer); + + // Tooltip mouseenter/mouseleave + mouseoverContainer.addEventListener("mouseenter", function() { + mouseoverContainer._isMouseOver = true; + // Clear any hide timeout when entering tooltip + if (mouseoverContainer._hideTimeout) { + clearTimeout(mouseoverContainer._hideTimeout); + mouseoverContainer._hideTimeout = null; + } + }); + mouseoverContainer.addEventListener("mouseleave", function() { + mouseoverContainer._isMouseOver = false; + // Hide after a short delay to allow for quick mouse movements + mouseoverContainer._hideTimeout = setTimeout(function() { + hideMouseoverText(mouseoverContainer); + }, 100); + }); } if (ele1) { ele1.setAttribute("mouseoverText", text); // Remove title attribute to prevent default browser tooltip if (ele1.title || ele1.dataset.tooltip) { ele1.setAttribute("originalTitle", ele1.title); ele1.title = ""; } // Remove previous listeners if any ele1.removeEventListener("mouseenter", ele1._mouseenterHandler); ele1.removeEventListener("mouseleave", ele1._mouseleaveHandler); // Show tooltip on mouseenter with delay ele1._mouseenterHandler = function(e) { // Clear any existing hide timeout @@ -4243,64 +4260,48 @@ // Clear show timeout if mouse leaves before delay if (ele1._tooltipShowTimeout) { clearTimeout(ele1._tooltipShowTimeout); ele1._tooltipShowTimeout = null; } // Use a grace period to allow moving to the tooltip itself ele1._tooltipHideTimeout = setTimeout(function() { if (!mouseoverContainer._isMouseOver) { hideMouseoverText(mouseoverContainer); } }, 500); // 500ms grace period }; ele1.addEventListener("mouseenter", ele1._mouseenterHandler); ele1.addEventListener("mouseleave", ele1._mouseleaveHandler); } - - // Tooltip mouseenter/mouseleave - mouseoverContainer.addEventListener("mouseenter", function() { - mouseoverContainer._isMouseOver = true; - // Clear any hide timeout when entering tooltip - if (mouseoverContainer._hideTimeout) { - clearTimeout(mouseoverContainer._hideTimeout); - mouseoverContainer._hideTimeout = null; - } - }); - mouseoverContainer.addEventListener("mouseleave", function() { - mouseoverContainer._isMouseOver = false; - // Hide after a short delay to allow for quick mouse movements - mouseoverContainer._hideTimeout = setTimeout(function() { - hideMouseoverText(mouseoverContainer); - }, 100); - }); } function showTooltipForElement(ele, ev) { // Show the tooltip for the given element if (suppressTooltips) {return;} + // wiggle mouseovers have special code, don't use these tooltips for those: + if (typeof mouseOver !== "undefined" && mouseOver.visible) {return;} let text = ele.getAttribute("mouseoverText"); if (!text) return; mouseoverContainer.replaceChildren(); let newEl = document.createElement("span"); newEl.style = "max-width: 400px"; newEl.innerHTML = text; mouseoverContainer.appendChild(newEl); positionMouseover(ev, ele, mouseoverContainer, ev.pageX, ev.pageY); mouseoverContainer.classList.add("isShown"); mouseoverContainer.style.opacity = "1"; mouseoverContainer.style.visibility = "visible"; - mouseoverContainer.setAttribute("origItemMouseoverId", ele.getAttribute("mouseoverid")); } function hideMouseoverText(ele) { /* Actually hides the tooltip text */ ele.classList.remove("isShown"); ele.style.opacity = "0"; ele.style.visibility = "hidden"; } function titleTagToMouseover(mapEl) { /* for a given area tag, extract the title text into a div that can be positioned * like a standard tooltip mouseover next to the item */ if (mapEl.dataset.tooltip) addMouseover(mapEl, mapEl.dataset.tooltip); else @@ -4316,67 +4317,80 @@ } if (a.title !== undefined && (a.title.startsWith("click & drag to scroll") || a.title.startsWith("drag select or click to zoom"))) a.title = ""; else if (a.title !== undefined && a.title.length > 0) { if (a.title.startsWith("Click to alter the display density")) { // these tooltips have a longer delay: a.setAttribute("tooltipDelay", "delayed"); } titleTagToMouseover(a); } }); /* Mouseover should clear if you leave the document window altogether */ document.body.addEventListener("mouseleave", (ev) => { + if (mouseoverContainer) { + hideMouseoverText(mouseoverContainer); + } + }); + + /* Mouseover should clear on scroll */ + document.addEventListener("scroll", (ev) => { + if (mouseoverContainer) { hideMouseoverText(mouseoverContainer); + } }); function hideTooltips() { + if (mouseoverContainer) { hideMouseoverText(mouseoverContainer); } + } /* make the mouseovers go away if we are in an input */ const inps = document.getElementsByTagName("input"); for (let inp of inps) { if (!(inp.type == "hidden" || inp.type == "HIDDEN")) { if (inp.type !== "submit") { inp.addEventListener("focus", hideTooltips); } else { // the buttons are inputs that don't blur right away (or ever? I can't tell), so // be sure to restore the tooltips when they are clicked inp.addEventListener("click", hideTooltips); } } } /* on a select, we can hide the tooltip on focus, but don't disable them * altogether, because it's easy to click out of a select without actually * losing focus, and we can't detect that because the web browser handles * that click separately */ const sels = document.getElementsByTagName("select"); for (let sel of sels) { sel.addEventListener("focus", hideTooltips); for (let opt of sel.options) { opt.addEventListener("click", hideTooltips); } } /* Make the ESC key hide tooltips */ document.body.addEventListener("keyup", (ev) => { if (ev.keyCode === 27) { + if (mouseoverContainer) { hideMouseoverText(mouseoverContainer); } + } }); } function parseUrl(url) { // turn a url into some of it's components like server, query-string, etc let protocol, serverName, pathInfo, queryString, queryArgs = {}; let temp; temp = url.split("?"); if (temp.length > 1) queryString = temp.slice(1).join("?"); temp = temp[0].split("/"); protocol = temp[0]; // "https:" serverName = temp[2]; // "genome-test.gi.ucsc.edu" pathInfo = temp.slice(3).join("/"); // "cgi-bin/hgTracks" cgi = pathInfo.startsWith("cgi-bin") ? pathInfo.split('/')[1] : "";