AniUI Academy

Position a tooltip inside the viewport

mediumDOM & Utilities+250 XPPractice

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 } with placement being "top" or "bottom".
  • Place the tooltip gap pixels above the anchor when there is room, defaulting gap to 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 = 0 when the tooltip is wider than the viewport, and round both coordinates with Math.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.
Asked atAtlassian

Stuck? The javascript course covers everything this challenge needs.

Try it yourself
Loading playground...
5 tests · +250 XP

Discussion

Sign in to post
Sign in to join the conversation.Sign in

No comments yet. Be the first to say something.