From 361cfa7584f62449477c57426f975528ebc1d24d Mon Sep 17 00:00:00 2001 From: Jeffrey Lewis Date: Mon, 7 Sep 2026 23:04:24 +0000 Subject: [PATCH] Fix the district search result tooltip and decide against touch support (Phase 2, site 6 of 6) Last of the 6 tooltip sites from #449, and structurally different from the other 5: a plain jQuery mouseover/mouseout on each search-result table row (not an SVG chart), showing the member's ideology score. Found a real, currently-live bug while looking at this one: the handler was `tr.on("mouseover", () => { ... $(this) ... })` -- an arrow function, which doesn't rebind `this` the way a normal function does. `this` inside resolved to the enclosing scope, not the row, so `$(this).children(...) .offset()` returned undefined and `.offset().left` threw a TypeError on every hover. The tooltip has never actually shown up. Confirmed by running the exact code (via a temporary local harness loading district.js directly against real /api/districtLookup data, removed after testing) before and after the fix. Fixed by using `tr`, already the correct row reference in scope, instead of `$(this)`; also dropped a leftover debug console.log that had the same bug and would have thrown first regardless. Considered, then deliberately skipped, adding touch-tap support here: each row already has its own click handler that navigates to the member's full bio page (same ideology score, fuller context). Testing confirmed a tap on touch navigates immediately -- so a competing tap-to- show-tooltip binding would show the tooltip for a moment and then get superseded by the navigation anyway, adding real complexity (the same one-dismiss-listener-for-N-rows split used in party.js/committee.js, needed here too since there can be 100+ result rows) for no actual benefit. Left it hover-only, matching mouse behavior; touch users reach the same information via the tap-to-navigate they already have. Verified with headless Chromium: - Desktop (mouse): hover now correctly shows the tooltip with content and hides on mouseout (previously threw and never appeared) - Touch: confirmed tapping a row still navigates to the member's page (unaffected -- nothing here was touching that handler) - Full existing 41-check suite (unrelated files, sanity check only) still passes This completes Phase 2 (#449) -- all 6 tooltip sites across the 5 listed files (vote map, party map + ideology chart, committee ideology chart, person ideology bucket, district search results) now handle touch appropriately, whether that means adding tap support or, as here, recognizing it wouldn't add anything. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_013KJMYfgTNsxrmjHZpZKAHT --- static/js/district.js | 65 ++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/static/js/district.js b/static/js/district.js index 0033c73a0..8a27df224 100644 --- a/static/js/district.js +++ b/static/js/district.js @@ -14,6 +14,10 @@ let cachedCoords = []; let initialLoad = 0; let markerPos; +function hideDistrictRowTooltip() { + $("#tooltipIdeology").css("visibility", "hidden"); +} + // From stackoverflow response, who borrowed it from Shopify--simple ordinal // suffix. function getGetOrdinal(n) { @@ -370,35 +374,40 @@ function buildResult(v, lastResult, tbody, myResults) { ${v["bioname"]}`) .appendTo(tr); - // Use a closure to pin tooltips onto each row. - ((v) => { - tr.on("mouseover", () => { - console.log($(this).children(".ideology").offset().left); - $("#tooltipIdeology").html(""); - const ideologyTooltip = `DW-NOMINATE: \ - ${v["nominate"]["dim1"]}
Scores from \ - -1 (Very Liberal) to 1 (Very Conservative)`; - if (v["nominate"] != undefined) { - $("#tooltipIdeology").html(ideologyTooltip); - } else { - $("#tooltipIdeology") - .html("No Ideology Score"); - } - - $("#tooltipIdeology").removeClass().addClass("d3-tip"); - $("#tooltipIdeology") - .css("left", - ($(this).children(".ideology").offset().left - - $("#tooltipIdeology").width() - 25) + "px"); + // Pin a tooltip to this row. (Previously used $(this) inside an + // arrow function, which doesn't rebind `this` -- it referred to + // whatever `this` was outside, not the row, so .offset() threw and + // the tooltip never actually appeared. Use `tr` directly instead, + // which is already the correct row for this closure.) + function showDistrictRowTooltip() { + $("#tooltipIdeology").html(""); + const ideologyTooltip = `DW-NOMINATE: \ + ${v["nominate"]["dim1"]}
Scores from \ + -1 (Very Liberal) to 1 (Very Conservative)`; + if (v["nominate"] != undefined) { + $("#tooltipIdeology").html(ideologyTooltip); + } else { $("#tooltipIdeology") - .css("top", $(this).offset().top + "px"); - $("#tooltipIdeology") - .css("visibility", "visible"); - }); - tr.on("mouseout", () => { - $("#tooltipIdeology").css("visibility", "hidden"); - }); - })(v); + .html("No Ideology Score"); + } + + $("#tooltipIdeology").removeClass().addClass("d3-tip"); + $("#tooltipIdeology") + .css("left", + (tr.children(".ideology").offset().left - + $("#tooltipIdeology").width() - 25) + "px"); + $("#tooltipIdeology") + .css("top", tr.offset().top + "px"); + $("#tooltipIdeology") + .css("visibility", "visible"); + } + // Touch has no hover, but doesn't need a tap-to-show alternative here + // the way the other tooltip sites do: this row is a full navigation + // link (see the click handler above), so a tap already takes a touch + // user to this member's own page -- with the same ideology score in + // fuller context -- before a tooltip could matter. + tr.on("mouseover", showDistrictRowTooltip); + tr.on("mouseout", hideDistrictRowTooltip); tr.appendTo(tbody);