D3.js: Position tooltips using element position, not mouse position?

Solution 1:

In your particular case you can simply use d to position the tooltip, i.e.

tooltip.html(d)  
  .style("left", d + "px")     
  .style("top", d + "px");

To make this a bit more general, you can select the element that is being moused over and get its coordinates to position the tooltip, i.e.

tooltip.html(d)  
  .style("left", d3.select(this).attr("cx") + "px")     
  .style("top", d3.select(this).attr("cy") + "px");

Solution 2:

Found something here that might address your problem even if <body> and <svg> have different positioning. This is assuming you have absolute position set for your tooltip.

.on("mouseover", function(d) {
    var matrix = this.getScreenCTM()
        .translate(+ this.getAttribute("cx"), + this.getAttribute("cy"));
    tooltip.html(d)
        .style("left", (window.pageXOffset + matrix.e + 15) + "px")
        .style("top", (window.pageYOffset + matrix.f - 30) + "px");
})

Solution 3:

In my experience, the easist solution is as follows:

First, getBoundingClientRect() to get the position of your element.

Then, use window.pageYOffset to adjust the height, relative to where you are.

E.g.

.on('mouseover', function(d) {
    let pos = d3.select(this).node().getBoundingClientRect();
    d3.select('#tooltip')
        .style('left', `${pos['x']}px`)
        .style('top', `${(window.pageYOffset  + pos['y'] - 100)}px`);
})

In the example above, I don't use X's offset because we rarely need to (unless you're scrolling horizontally).

Adding window.pageYOffset and pos['y'] gives us the current mouse position (wherever we are on the page). I subtract 100 to place the tooltip a little above it.