Search code examples
angularsvgtooltipangular-renderer2angular-gridster2

Wrong coordinates of HTML elements obtained in components


I've added a custom tooltip to an SVG chart within gridster item (all examples are provided on stackblitz). Tooltip (div) coordinates are obtained using nativeElement and set using the Renderer2 (trivial example - tooltip div is offset by 20px). However, when I project the chart widget within gridster item, the tooltip is always added to the wrong position (see the full example - hover over circles). It seems that for every widget, the coordinates of the div are incremented by how much that widget is far from the edges of the window (left and top).

Visually, this is how far the tooltip should be away from the circle:

Normal

But in reality (the more the element is far away from the edge of the screen, the further the tooltip is placed):

Not normal

I've tried to see if this is the problem with the <ng-content> i.e. the content projection within GridsterItem, but it doesn't seem to me like that when I make a custom component to mimic that (see this example). Why are coordinates funky within gridster item? Is renderer setting coordinates relative to the widget, or to the document?


Solution

  • The problem is that you set the tooltip position based on the client position of the circle. What you need is the position of the circle relative to its parent.

    public mouseEnter($event, data): void {
      const circle = $event.target as HTMLElement;
      const parent = circle.parentElement;
      const circleCoords = circle.getBoundingClientRect();
      const parentCoords = parent.getBoundingClientRect();
      const relativeX = circleCoords.left - parentCoords.left;
      const relativeY = circleCoords.top - parentCoords.top;
      const x = `${relativeX + 20}px`;
      const y = `${relativeY + 20}px`;
      this.renderer.setStyle(this.tooltip.nativeElement, 'left', x);
      this.renderer.setStyle(this.tooltip.nativeElement, 'top', y);
      this.renderer.setStyle(this.tooltip.nativeElement, 'display', 'block');
      this.renderer.setProperty(this.tooltip.nativeElement, 'innerHTML', data);
    }
    

    See this stackblitz for a demo.