简体   繁体   中英

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')
      ...
      .on('mouseover', function (d) {
        var foHeight = rect_height;
        var foWidth = rect_width;
        var t = 50, k = 15;
        var tip = {'w': (3/4 * t), 'h': k};
        var anchor = {'w': 100/3, 'h': 100/3};

        var fo = svg.append('foreignObject')
          .attr('x', 0)
          .attr('y', 0)
          .attr('width', rect_width)
          .attr('height', rect_height)
          .attr('class', 'svg-tooltip');

        var div = fo.append('xhtml:div')
           .append('div')
           .attr('class', 'tooltip');

        console.log('div is :');
        console.log(div);

        div.append('p').html(function() {
          console.log('dev append d is');
          console.log(d);

          if(d.data.tooltip || d.data.name) {
            console.log('we have tooltip or name.');
            console.log('name is' + d.data.name);

            return '<div style="z-index:2 ; width: '
              + (125) + 'px; height: '
              + (42) + 'px;" class="node-text wordwrap" title="' + (d.data.tooltip || d.data.name) + '">'
              + '</div>';
          }
        });

        fo.attr('height', 200);

        svg.insert('polygon', '.svg-tooltip')
          .attr({
            'points': "0,0 0," + foHeight + " " + foWidth + "," + foHeight + " " + foWidth + ",0 " + (t) + ",0 " + tip.w + "," + (-tip.h) + " " + (t/2) + ",0",
            'height': foHeight + tip.h,
            'width': foWidth,
            'fill': '#D8D8D8',
            'opacity': 0.75,
            'transform': 'translate(' + (anchor.w - tip.w) + ',' + (anchor.h + tip.h) + ')'
          })
     })    
    .on('mouseout', function(d) {
        console.log('mouse out of ' + d.data.name);
        svg.selectAll('.svg-tooltip').remove();
        svg.selectAll('polygon').remove();
    });

When I place the mouse on one of my nodes I see this output in my developer's console ...

div is :
Selection {_groups: Array(1), _parents: Array(1)}
dev append d is
Node {data: {…}, height: 0, depth: 2, parent: Node, x: 150, …}
have tooltip or name.
name isquoting-es-stg1-001

... but no tooltip appears. What am I doing wrong?

Thanks!

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) { return Math.floor(i/4) * (rectHeight+2); }) .attr("width",rectWidth) .attr("height",rectHeight) .attr("fill", function(d,i) { return color((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: 5px 0px; } polygon { pointer-events: none; } 
 <script src="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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM