Position a tooltip inside the viewport
This is the core of every popover library. You know where the trigger is and how big the tooltip is; you need coordinates that keep the tooltip on screen. Real code measures with getBoundingClientRect(), but the placement decision itself is pure arithmetic over those numbers.
Implement placeTooltip(anchor, tooltip, viewport, gap = 8). anchor is { x, y, width, height } in viewport coordinates, tooltip is { width, height }, and viewport is { width, height }. Return { x, y, placement }.
Vertically, prefer above: y = anchor.y - tooltip.height - gap, with placement of "top". If that y is less than 0 there is no room, so flip below with y = anchor.y + anchor.height + gap and placement of "bottom". The flipped position is not clamped further — if it also overflows, it stays below.
Horizontally, centre on the anchor: x = anchor.x + anchor.width / 2 - tooltip.width / 2, then clamp into the range 0 to viewport.width - tooltip.width. If the tooltip is wider than the viewport that range is empty, so use x = 0. Both returned coordinates are rounded with Math.round.
Arguments
anchor{ x: number; y: number; width: number; height: number }- The trigger's bounding rect in viewport coordinates.
tooltip{ width: number; height: number }- The tooltip's measured size.
viewport{ width: number; height: number }- The viewport size the tooltip has to fit inside horizontally.
gapnumber- Pixel gap between the trigger and the tooltip. Defaults to 8.
Returns
{ x: number; y: number; placement: "top" | "bottom" } — Rounded viewport coordinates for the tooltip's top-left corner plus the placement chosen.
Examples
placeTooltip(
{ x: 100, y: 200, width: 40, height: 20 },
{ width: 100, height: 30 },
{ width: 800, height: 600 }
);
// => { x: 70, y: 162, placement: "top" }Default placement is above the trigger, centred horizontally.
placeTooltip(
{ x: 100, y: 10, width: 40, height: 20 },
{ width: 100, height: 30 },
{ width: 800, height: 600 }
);
// => { x: 70, y: 38, placement: "bottom" }When there is no room above, the tooltip flips below.
placeTooltip(
{ x: 0, y: 200, width: 20, height: 20 },
{ width: 120, height: 30 },
{ width: 800, height: 600 }
);
// => { x: 0, y: 162, placement: "top" }The tooltip is clamped against the left viewport edge.
What it has to do
- Return
{ x, y, placement }withplacementbeing"top"or"bottom". - Place the tooltip
gappixels above the anchor when there is room, defaultinggapto 8. - Flip below the anchor when the top position would be above 0.
- Centre horizontally on the anchor, then clamp into
0 .. viewport.width - tooltip.width. - Use
x = 0when the tooltip is wider than the viewport, and round both coordinates withMath.round.
Notes
- A real popover library — Floating UI, Popper — also handles the bottom edge, the right edge, arrow placement and transforms. Those are each a layer on top of this basic flip-and-clamp.
- Clamping the horizontal position but letting the flipped vertical position overflow is a deliberate asymmetry: horizontal shifts don't hurt usability, but a tooltip that jumps from above to below shouldn't then shift to a third location.
Stuck? The javascript course covers everything this challenge needs.
- 1.centres above the anchor when there is room
- 2.flips below when the top would overflow
- 3.clamps against the left edge
- 4.clamps against the right edge
- 5.falls back to zero when the tooltip is wider than the viewport
These assertions run in your browser the moment you hit Run. Pass all of them to claim the XP.
Discussion
Sign in to postNo comments yet. Be the first to say something.