0

Have a stand alone SVG to which I want to add a tooltip (stand alone meaning: it's not in an HTML document and not controlled by styles external to the SVG).

The SVG will be injected into HTML, together with other SVGs of a similar type (based on a database query). For example, an SVG will be displayed "center,center" in an table cell. The table, for example, will have 5 columns and 4 rows (20 SVGs total)

Here's what I have so far:

<svg
  xmlns="http://www.w3.org/2000/svg"
  viewBox="0 0 300 200"
  id="tooltip-svg-1"
>
<style>
  #tooltip {dominant-baseline: hanging;}
</style>

<rect x="-20" y="10" width="80" height="80" fill="#007bbf" class="tooltip-trigger" data-tooltip-text="Short-text"/>

<g id="tooltip" visibility="hidden" >
<rect x="2" y="2" width="80" height="24" fill="black" opacity="0.4" rx="2" ry="2"/>
<rect width="80" height="24" fill="white" rx="2" ry="2"/>
<text x="3" y="6">Tooltip</text>
</g>

<script type="text/ecmascript"><![CDATA[
    (function() {
      var svg = document.getElementById('tooltip-svg-1');
      var tooltip = svg.getElementById('tooltip');
      var tooltipText = tooltip.getElementsByTagName('text')[0].firstChild;
      var triggers = svg.getElementsByClassName('tooltip-trigger');

      for (var i = 0; i < triggers.length; i++) {
        triggers[i].addEventListener('mousemove', showTooltip);
        triggers[i].addEventListener('mouseout', hideTooltip);
      }

      function showTooltip(evt) {
        var CTM = svg.getScreenCTM();
        var x = (evt.clientX - CTM.e + 6) / CTM.a;
        var y = (evt.clientY - CTM.f + 20) / CTM.d;
        tooltip.setAttributeNS(null, "transform", "translate(" + x + " " + y + ")");
        tooltip.setAttributeNS(null, "visibility", "visible");
        tooltipText.data = evt.target.getAttributeNS(null, "data-tooltip-text");
      }

      function hideTooltip(evt) {
        tooltip.setAttributeNS(null, "visibility", "hidden");
      }
    })()
]]>
</script>
</svg>

Now would like to include a PNG in the center of an SVG

Have added xmlns:xlink="http://www.w3.org/1999/xlink" to the SVG header.

Then inject a child-SVG as follows:

<svg viewBox="0 0 760 760" x="-34" y="15">
<image xlink:href="http://lorempixel.com/output/city-q-c-256-256-5.jpg" width="256px" height="256px"/>
</svg>

Here is that implementation:

<svg
  xmlns="http://www.w3.org/2000/svg"
  xmlns:xlink="http://www.w3.org/1999/xlink"
  viewBox="0 0 300 200"
  id="tooltip-svg-2"
>

<style>
  #tooltip {dominant-baseline: hanging;}
</style>

<defs>
<radialGradient id="RadialGradient-1" cx="0.5" cy="0.5" r="2">
<stop offset="0.00" style="stop-color:#ffffff; stop-opacity:1;" />
<stop offset="1.00" style="stop-color:#696969; stop-opacity:1;" />
</radialGradient>
</defs>

<rect
  style="fill:url(#RadialGradient-1)"
  rx="10"
  ry="10"
  x="10"
  y="10"
  width="80"
  height="80"
  class="tooltip-trigger"
  data-tooltip-text="label with several words"
/>

<svg viewBox="0 0 760 760" x="-34" y="15">
<image xlink:href="http://lorempixel.com/output/city-q-c-256-256-5.jpg" width="256px" height="256px"/>
</svg>

<g id="tooltip" visibility="hidden" >
<rect x="2" y="2" width="200" height="24" fill="black" opacity="0.4" rx="2" ry="2"/>
<rect width="200" height="24" fill="white" rx="2" ry="2"/>
<text x="3" y="6">Tooltip</text>
</g>

<script type="text/ecmascript">
<![CDATA[
    (function() {
      var svg = document.getElementById('tooltip-svg-2');
      var tooltip = svg.getElementById('tooltip');
      var tooltipText = tooltip.getElementsByTagName('text')[0].firstChild;
      var triggers = svg.getElementsByClassName('tooltip-trigger');
      for (var i = 0; i< triggers.length; i++) {
        triggers[i].addEventListener('mousemove', showTooltip);
        triggers[i].addEventListener('mouseout', hideTooltip);
      }

      function showTooltip(evt) {
        var CTM = svg.getScreenCTM();
        var x = (evt.clientX - CTM.e + 6) / CTM.a;
        var y = (evt.clientY - CTM.f + 20) / CTM.d;
        tooltip.setAttributeNS(null, "transform", "translate(" + x + " " + y + ")");
        tooltip.setAttributeNS(null, "visibility", "visible");
        tooltipText.data = evt.target.getAttributeNS(null, "data-tooltip-text");
      }

      function hideTooltip(evt) {
        tooltip.setAttributeNS(null, "visibility", "hidden");
      }
    })()
]]>
</script>
</svg>

All PNGs have the same dimensions: width="256px" height="256"px

All SVG <rect> have the same dimensions: width="80" height="80"

But there are two issues:

1 as you can see, i've munged the child-SVG viewBox and x,y coordinates to position PNG in the parent-SVG.

2 the tooltip does not hover over the PNG addition (the child-SVG); it displays only on the border of the parent-SVG.

Have two questions:

1 How to properly configure/relate the child-SVG (containing the PNG) to the parent-SVG (the rectangle)? In other words, how to combine the two systematically (where the position of the child-SVG is relative to the parent-SVG), as opposed to the hack that manually adjusts the child relative to the parent?

2 How to modify the <g id="tooltip"> defining the tooltip so that text is visible when hovering over the child-SVG (with the PNG), as well as the parent-SVG?

Jay Gray
  • 1,706
  • 2
  • 20
  • 36
  • Have you seen https://stackoverflow.com/questions/22183727/how-do-you-convert-screen-coordinates-to-document-space-in-a-scaled-svg – Robert Longson Aug 11 '20 at 12:54
  • Thanks for the tip @RobertLongson. I'll study it further. But my alt issues are the child-SVG relative to the parent-SVG, and failing to understand why the PNG is impeding the tooltip. Unless I'm missing some information in your link ... if so, please redirect me. – Jay Gray Aug 11 '20 at 13:02
  • perhaps you need to set pointer-events: none on the png. – Robert Longson Aug 11 '20 at 14:19
  • i'll learn how to do that. – Jay Gray Aug 11 '20 at 14:34

0 Answers0