47

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?

Ricardo
  • 3,696
  • 5
  • 36
  • 50
Richard
  • 62,943
  • 126
  • 334
  • 542
  • If you're ok with any kind of tooltip, [this answer](http://stackoverflow.com/questions/10805184/d3-show-data-on-mouseover-of-circle/10806220#10806220) should help. – Lars Kotthoff Apr 27 '13 at 20:23
  • Thanks! I'd prefer to use pure D3 tooltips rather than tipsy, if possible. – Richard Apr 27 '13 at 20:56

4 Answers4

32

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");
Lars Kotthoff
  • 107,425
  • 16
  • 204
  • 204
  • 2
    Thanks - I'm afraid using `d3.select(this).attr("cx")` only works if the `` and `` elements have the same positioning. See http://jsfiddle.net/WLYUY/6/ – Richard Apr 27 '13 at 21:11
  • 4
    If you have other content, you have to add the offset of the SVG to the position, see http://jsfiddle.net/WLYUY/7/ – Lars Kotthoff Apr 27 '13 at 21:29
  • 1
    The solution with offset worked for me. Thanks Lars. Now I use the visualization within a bootstrap column grid, but the offset is not affected by the new positioning. How can I fix this? – Robin Wieruch May 26 '14 at 11:32
  • Not sure, would need to see a complete example. You may want to ask a separate question about this. – Lars Kotthoff May 26 '14 at 11:33
  • 2
    I came up with this: var matrix = this.getScreenCTM().translate(+this.getAttribute("cx"),+this.getAttribute("cy"));tip.style("left", (window.pageXOffset + matrix.e) + "px").style("top", (window.pageYOffset + matrix.f + 30) + "px"); – Robin Wieruch May 26 '14 at 11:44
20

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");
})
Boxuan
  • 4,937
  • 6
  • 37
  • 73
6

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.

Jared Wilber
  • 6,038
  • 1
  • 32
  • 35
1

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");})
timemirror
  • 586
  • 4
  • 11