Search code examples
javascriptd3.jssvgforeignobject

Append HTML to existing tooltip in d3


I am trying to modify the the tooltip in zalando's tech radar.

The relevant code is:

  function showBubble(d) {
    if (d.active || config.print_layout) {
      var tooltip = d3.select("#bubble text")
        .text(d.label);
      var bbox = tooltip.node().getBBox();
      d3.select("#bubble")
        .attr("transform", translate(d.x - bbox.width / 2, d.y - 16))
        .style("opacity", 0.8);
      d3.select("#bubble rect")
        .attr("x", -5)
        .attr("y", -bbox.height)
        .attr("width", bbox.width + 10)
        .attr("height", bbox.height + 4);
      d3.select("#bubble path")
        .attr("transform", translate(bbox.width / 2 - 5, 3));
    }
  }

In order to extend the tooltip I tried doing the following based on the solution described here.

My modified code:

function showBubble(d) {
    if (d.active || config.print_layout) {
      var tooltip = d3.select("#bubble text");
      tooltip.html("foo"); // this works!
      //tooltip.html(function(d) { d.label}) // d is undefinded here ...
      tooltip.append("div").attr("id", "foo");

      d3.select("#foo").html("This is not shown").attr("style", "block");

      var bbox = tooltip.node().getBBox();

      d3.select("#bubble")
        .attr("transform", translate(d.x - bbox.width / 2, d.y - 16))
        .style("opacity", 0.8);
      d3.select("#bubble rect")
        .attr("x", -5)
        .attr("y", -bbox.height)
        .attr("width", bbox.width + 10)
        .attr("height", bbox.height + 4);
      d3.select("#bubble path")
        .attr("transform", translate(bbox.width / 2 - 5, 3));
    }
  }

Can someone give me a hint how to show this extra text?

update

the complete code https://github.com/zalando/tech-radar


Solution

  • Multiline text in svg works a little different than HTML. You can't just append <div> & <br> tags because they don't mean anything in SVG.

    So your options are to:

    var tooltip = d3.select("#bubble")
    var fo = tooltip.append('foreignObject').attr('width', '100%').attr('height', '100%')
    var foDiv = fo.append("xhtml:body").append("xhtml:div").attr('class', 'fe-div').style('background', '#ccc').html("foo <br>2nd line <br>3rd line")
    html,
    body,
    svg {
      height: 100%
    }
    
    .fe-div {
      color: blue;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
    <svg viewBox="0 0 240 80" xmlns="http://www.w3.org/2000/svg">
      <g id="bubble">
      </g>
    </svg>

    • or use positioned tspan elements to break up text like so:

    var tooltip = d3.select("#bubble text");
    tooltip.html("foo"); // this works!
    
    // Create a tspan for the 2nd line
    var tspan1 = tooltip.append("tspan");
    tspan1.html("2nd line");
    tspan1.attr('x', 0).attr('dy', '1em')
    
    // Create a tspan for the 3rd line
    var tspan2 = tooltip.append("tspan");
    tspan2.html("3rd line");
    tspan2.attr('x', 0).attr('dy', '1em')
    html,
    body,
    svg {
      height: 100%
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
    <svg viewBox="0 0 240 80" xmlns="http://www.w3.org/2000/svg">
      <g id="bubble">
        <text y="35"></text>
      </g>
    </svg>