Richard
Richard

Reputation: 65530

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

I'm using D3 to draw a scatter graph. I would like to show tooltips when the user mouses over each circle.

My problem is that I can append tooltips, but they're positioned using the mouse event d3.event.pageX and d3.event.pageY, so they are not positioned consistently over each circle.

Instead, some are slightly to the left of the circle, some to the right - it depends on how the user's mouse enters the circle.

This is my code:

circles
  .on("mouseover", function(d) {         
    tooltip.html(d)  
      .style("left", (d3.event.pageX) + "px")     
      .style("top", (d3.event.pageY - 28) + "px");    
  })                  
  .on("mouseout", function(d) {       
    tooltip.transition().duration(500).style("opacity", 0);   
  });

And is a JSFiddle showing the problem: http://jsfiddle.net/WLYUY/5/

Is there some way I can use the centre of the circle itself as the position to orient the tooltip, not the mouse position?

Upvotes: 47

Views: 77442

Answers (4)

Jared Wilber
Jared Wilber

Reputation: 6795

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.

Upvotes: 6

timemirror
timemirror

Reputation: 596

I'm new to D3 so this may not work for scatterplots... but found it seems to work for Bar charts... where v1 and v2 are the values being plotted.. and it seems to look up the value from the data array.

.on("mouseover", function(d) {
                  divt .transition()
                      .duration(200)
                      .style("opacity", .9);
                  divt .html(d.v1)
                      .style("left", x(d.v2)+50 + "px")
                      .style("top",y(d.v1)+ "px");})

Upvotes: 1

Boxuan
Boxuan

Reputation: 5157

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");
})

Upvotes: 20

Lars Kotthoff
Lars Kotthoff

Reputation: 109232

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");

Upvotes: 32

Related Questions