Skip to content Skip to sidebar Skip to footer

D3 Tree Tooltip Does Not Appear

I have been trying to add a tooltip to the nodes of my d3 tree. I have looked at this example and I have based this bit of my code on that example: nodeEnter.append('rect')

Solution 1:

There are a few issues with the code here. The example isn't the easiest to adapt because:

  • The location of the tooltip is hard coded (anchor = {'w': width/3, 'h': height/3})
  • The snippet uses d3v3 .attr() with objects (as noted earlier)
  • It isn't particularily clear what some of the variables represent.

First we want to know where the tooltip should be placed, I went with:

.on('mouseover', function (d) {
    // Get highlight rectangle:var rect = d3.select(this); // the selected rect.// Get bottom middle of rectangle:var x = +rect.attr("x") + rectWidth/2;
    var y = +rect.attr("y") + rectHeight;

    ...

This gets the middle of the bottom edge of the mouse-overed rectangle.

We need to apply this to both the foreign object (which contains the text) and the polygon (which contains the shading + shape - which could be done in pure css without an svg polygon):

 // for the foreign object:
 var fo = svg.append('foreignObject')
    .attr('x', x)
    .attr('y', y)

// And for the polygon:
.attr("transform","translate("+[x-tip.w,y+tip.h/2]+")");

If we replace where .attr() appears with an object with either individidual .attr("property",value) lines or use d3.selection-multi and .attrs(), then we should have something working.

Lastly, foWidth and foHeight probably aren't meant to be the same as rectWidth and rectHeight, otherwise any tooltip larger than your rectangles will have text cut off. Although, you do overwrite the width for the foreign object to 200, so it isn't rectWidth

I believe these are all the changes I've made below:

var rectWidth = 28;
  var rectHeight = 28;
  
  var svg = d3.select("body")
    .append("svg")
    .attr("height", 300);
    
  var data = d3.range(16);
  var color = d3.interpolateBlues;
  
  var nodeEnter = svg.selectAll(null)
    .data(data)
    .enter()
    .append("rect")
    .attr("x", function(d,i) {
      return i%4 * (rectWidth+2);
    })
    .attr("y", function(d,i) {
      returnMath.floor(i/4) * (rectHeight+2);
    })
    .attr("width",rectWidth)
    .attr("height",rectHeight)
    .attr("fill", function(d,i) {
      returncolor((Math.floor(i/4)+1) * (i%4+1) /16)
    })
    .on('mouseover', function (d) {
        // Get highlight rectangle:var rect = d3.select(this); // the selected rect.// Get bottom middle of rectangle:var x = +rect.attr("x") + rectWidth/2;
        var y = +rect.attr("y") + rectHeight;
        
        // Dimensions of foreign object:var foHeight = 50;
        var foWidth = 200;
        
        // tooltip triangle info:var t = 50, k = 15;
        var tip = {'w': (3/4 * t), 'h': k};

        //Foreign  object:var fo = svg.append('foreignObject')
          .attr('x', x)
          .attr('y', y)
          .attr('width', foWidth)
          .attr('height', foHeight)
          .attr('class', 'svg-tooltip')
         
        // FO's Div:var div = fo.append('xhtml:div')
           .append('div')
           .attr("class","tooltip");

        // Div's p:
        div.append('p').html(function() {
            return"This is square: " + d; 
        })
        
        // SVG polygon that creates the background for the FO:
        svg.insert('polygon', '.svg-tooltip')
          .attr('points', "0,0 0," + foHeight + " " + foWidth + "," + foHeight + " " + foWidth + ",0 " + (t) + ",0 " + tip.w + "," + (-tip.h) + " " + (t/2) + ",0")
           .attr('height', foHeight)
           .attr('width',foWidth)
           .attr('fill','#D8D8D8')
           .attr('opacity',0.75)
           .attr('transform',"translate("+[x-tip.w,y+tip.h/2]+")")
    }).on('mouseout', function() {
        svg.selectAll('.svg-tooltip').remove();
        svg.selectAll('polygon').remove();       
    })
svg {
                display: block;
                margin: 0 auto;
            }
            .svg-tooltip {
                pointer-events: none;
            }
            .tooltip {
                padding: 10px;
                color: #4A22FF;
            }
            .lead {
                font-style: italic;
            }
            p {
                margin: 5px0px;
            }
            polygon {
                pointer-events: none;
            }
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

However the foreign object/svg based tooltip has limitations. Unless the tooltip is anchored from the bottom when approaching the bottom of the SVG, it may be cut off. Tooltips that position a div (not a foreign object containing a div) over the SVG where the mouse is will help in this regard. D3noob has a good example of this.

Post a Comment for "D3 Tree Tooltip Does Not Appear"