Pen Settings



CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource


Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource


Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.


Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.


                <h1>Positioning a Tooltip on a SVG</h1>
  SVG graphics are made up of DOM elements, and can respond to user events: for example, a mouseover event listener can be used to trigger a tooltip display.  But getting that tooltip where you want it is complicated by the complex SVG coordinate system.  Mousing over any of the coloured circles below will cause six tooltips to appear (seven including the browser's title text), each positioned with a different method.
<svg viewBox="0 0 400 300">
    <g id="circle-group" 
       transform="rotate(10) translate(20,-5)">
        <circle r="25" cx="50" cy="50" fill="red"/>
        <g class="tooltip css" transform="translate(50,50)">
          <rect x="-3em" y="-45" width="6em" height="1.25em"/>
          <text y="-45" dy="1em" text-anchor="middle" fill="red">
            SVG/CSS Tip</text>
        <circle r="25" cx="200" cy="50" fill="LightSeaGreen"/>
        <g class="tooltip css" transform="translate(200,50)">
          <rect x="-3em" y="-45" width="6em" height="1.25em"/>
          <text y="-45" dy="1em" text-anchor="middle" fill="LightSeaGreen">
            SVG/CSS Tip</text>
        <circle r="25" cx="350" cy="50" fill="orange"/>
        <g class="tooltip css" transform="translate(350,50)">
          <rect x="-3em" y="-45" width="6em" height="1.25em"/>
          <text y="-45" dy="1em" text-anchor="middle" fill="orange">
            SVG/CSS Tip</text>
        <circle r="25" cx="50" cy="200" fill="cornflowerblue"/>
        <g class="tooltip css" transform="translate(50,200)">
          <rect x="-3em" y="-45" width="6em" height="1.25em"/>
          <text y="-45" dy="1em" text-anchor="middle" fill="cornflowerblue">
            SVG/CSS Tip</text>
        <circle r="25" cx="200" cy="200" fill="DarkOrchid"/>
        <g class="tooltip css" transform="translate(200,200)">
          <rect x="-3em" y="-45" width="6em" height="1.25em"/>
          <text y="-45" dy="1em" text-anchor="middle" fill="DarkOrchid">
            SVG/CSS Tip</text>
        <circle r="25" cx="350" cy="200" fill="ForestGreen"/>
        <g class="tooltip css" transform="translate(350,200)">
          <rect x="-3em" y="-45" width="6em" height="1.25em"/>
          <text y="-45" dy="1em" text-anchor="middle" fill="ForestGreen">
            SVG/CSS Tip</text>
    <g transform="scale(1.2)">
        <g class="tooltip exact">
            <rect width="4em" height="1.25em"/>
            <text dy="1em" x="2em" text-anchor="middle">
              SVG Tip</text>
    <g transform="scale(0.9)">
        <g class="tooltip mouse">
            <!-- The rectangle and text are positioned 
                 to the right and above the <g> element's
                 0,0 point, purely to help with all the 
                 overlapping tooltips! -->
          <rect width="7em" height="2.5em"  
                  x="-7em" y="-2.5em"/>
          <text x="-3.5em" y="-2.5em" text-anchor="middle">
            <tspan dy="1em">Mouse-tracking</tspan>
            <tspan x="-3.5em" dy="1.25em">SVG Tip</tspan>
<div class="absolute tooltip">Absolute HTML Tip</div>
<div class="fixed tooltip">Fixed HTML Tip</div>
<div class="mouse tooltip" >Mouse-tracking HTML Tip</div>
<button id="wiggle" type="button">Wiggle the SVG</button>

<p>A quick run-down of the pros and cons of each type of tooltip:</p>
  <dt>Title text tooltips</dt>
  <dd><i>Pro:</i> Quick and easy, just insert a <code>&lt;title></code> element within the SVG element that you want to trigger the tooltip.  (Some browsers will display a tooltip if you use the title attribute, similar to how HTML title tooltips work, but Webkit browsers require you to use an SVG <code>&lt;title></code> element.)  They are also semantic and screen-reader friendly and can be hard-coded in Javascript-free SVG. <br/>
  <i>Con:</i> Ugly and out of your control.
  <dt>SVG tooltips</dt>
  <dd><i>Pro:</i> Contained within your SVG code, scale with the SVG.  <br/>
  <i>Con:</i> An opaque background requires a separate rectangle element, which is difficult to size properly with the text while remaining responsive to user's font preferences (although you can query the text length with Javascript, and size the rectangle accordingly, I didn't do that here); line breaks have to be hard-coded as separately-positioned text spans.  Webkit browsers don't respect "overflow:visible" on SVG, so the tip will be cropped if it overlaps the edge of the image.
  <dt>HTML tooltips</dt>
  <dd><i>Pro:</i> The preferred choice; an absolutely positioned <code>&lt;div&gt;</code> that can contain formatted, wrapping text.  <br/>
  <i>Con:</i> You have to figure out how to convert between the SVG coordinate system and the page coordinates you use for absolutely positioning your tooltip.  But that's why you're here.  It's all commented up in the code!
<p>And the pros and cons of each positioning method:</p>
  <dt>CSS hover-revealed tooltips</dt>
  <dd><i>Pro:</i> No Javascript required, position them where you want them when you create the SVG graphic.  <br/>
  <i>Con:</i> Requires a separate tooltip for every object, so your code gets repetitive.  The tip has to be a sibling of the element that triggers it, so it has to be an SVG tip, with the corresponding formatting limitations.  For pro or con, it will also be caught up in any transformations (e.g. rotations, scale) in that coordinate system.
  <dt>Mouse-tracking tooltips</dt>
  <dd><i>Pro:</i> Easier to position, and the user can move the mouse if they block something underneath.  <br/>
  <i>Con:</i> Kind of annoying, floating around difficult to read.  More importantly, they react to every mousemove event, slowing down the code.
  <dt>Positioned tooltips</dt>
  <dd><i>Pro:</i> Neat and exact, and you can position them in an appropriate location relative to the underlying content.  <br/>
  <i>Con:</i> If the user doesn't agree with your determination of the appropriate position, they can't do anything about it.
<p> If you want to confirm that the positions of the tooltips adjust to the current state of the SVG, press "Wiggle the SVG" to randomly shift and tilt the circles or resize the browser to change the SVG scale.  Note, however, that if you have any tooltips visible during the shift, I don't currently have any code to update their positions as things move.</p>
<p><b><i>P.S.</i></b>  If you're in "Edit pen" view and are only getting the CSS tooltip, press the big blue "Run" button on the Javascript panel. </p>


                .tooltip {
    pointer-events:none; /*let mouse events pass through*/
    transition: opacity 0.3s;
    text-shadow:1px 1px 0px gray;

div.tooltip {
    background: lightblue;
    border:solid gray;
    position: absolute;
    max-width: 8em;
div.fixed {
g.tooltip:not(.css) {
g.tooltip rect {
    fill: lightblue;
    stroke: gray;
circle:hover + g.tooltip.css {
svg {
    margin:10px 20px;
    width:calc(100% - 40px);
    border: solid 1px black;
    background-color: #222;
    /*allow tooltips to spill into margins */
  margin: 0 auto 20px;
  padding: 0.5em;
  box-shadow: 0 0 2px 2px navy;


body {
  max-width: 40em;
  background: lightyellow;
p, dl {
  margin: 0.5em 1.5em;
dt {


                var tooltip = d3.selectAll(".tooltip:not(.css)");
var HTMLabsoluteTip ="div.tooltip.absolute");
var HTMLfixedTip ="div.tooltip.fixed");
var HTMLmouseTip ="div.tooltip.mouse");
var SVGexactTip ="g.tooltip.exact");
var SVGmouseTip ="g.tooltip.mouse");
/* If this seems like a lot of different variables,
   remember that normally you'd only implement one 
   type of tooltip! */

/* I'm using d3 to add the event handlers to the circles
   and set positioning attributes on the tooltips, but
   you could use JQuery or plain Javascript. */
var circles ="svg").select("g")

    /***** Easy but ugly tooltip *****/ 
      .text("Automatic Title Tooltip");

circles.on("mouseover", function () {"opacity", "1");
        /* You'd normally set the tooltip text
           here, based on data from the  element
           being moused-over; I'm just setting colour. */"color", this.getAttribute("fill") );
      /* Note: SVG text is set in CSS to link fill colour to 
         the "color" attribute. */
        /***** Positioning a tooltip precisely
               over an SVG element *****/ 
        /***** For an SVG tooltip *****/ 
        //"this" in the context of this function
        //is the element that triggered this event handler
        //which will be one of the circle elements.
        var tooltipParent = SVGexactTip.node().parentNode;
        var matrix = 
        //getTransformToElement returns a matrix
        //representing all translations, rotations, etc.
        //to convert between two coordinate systems.
        //The .translate(x,y) function adds an additional 
        //translation to the centre of the circle.
        //the matrix has values a, b, c, d, e, and f
        //we're only interested in e and f
        //which represent the final horizontal and vertical
        //translation between the top left of the svg and 
        //the centre of the circle.
        //we get the position of the svg on the page
        //using this.viewportElement to get the SVG
        //and using offsetTop and offsetLeft to get the SVG's
        //position relative to the page.
            .attr("transform", "translate(" + (matrix.e)
                      + "," + (matrix.f-20) + ")");
        /***** For an HTML tooltip *****/ 
        //for the HTML tooltip, we're not interested in a
        //transformation relative to an internal SVG coordinate
        //system, but relative to the page body
        //We can't get that matrix directly,
        //but we can get the conversion to the
        //screen coordinates.
        var matrix = this.getScreenCTM()
        //You can use screen coordinates directly to position
        //a fixed-position tooltip        
                   (matrix.e) + "px")
                   (matrix.f + 3) + "px");
        //The limitation of fixed position is that it won't
        //change when scrolled.
        //A better solution is to calculate the position 
        //of the page on the screen to position an 
        //absolute-positioned tooltip:
                   (window.pageXOffset + matrix.e) + "px")
                   (window.pageYOffset + matrix.f + 30) + "px");
    .on("mousemove", function () {
        /***** Positioning a tooltip using mouse coordinates *****/ 
        /* The code is shorter, but it runs every time
           the mouse moves, so it could slow down other
           processes or animation. */
        /***** For an SVG tooltip *****/ 
        var mouseCoords = d3.mouse(
        //the d3.mouse() function calculates the mouse
        //position relative to an SVG Element, in that 
        //element's coordinate system 
        //(after transform or viewBox attributes).
        //Because we're using the coordinates to position
        //the SVG tooltip, we want the coordinates to be
        //with respect to that element's parent.
        //SVGmouseTip.node() accesses the (first and only)
        //selected element from the saved d3 selection object.
            .attr("transform", "translate("
                  + (mouseCoords[0]-10) + "," 
                  + (mouseCoords[1] - 10) + ")");
        /***** For an HTML tooltip *****/ 
        //mouse coordinates relative to the page as a whole
        //can be accessed directly from the click event object
        //(which d3 stores as d3.event)
            .style("left", Math.max(0, d3.event.pageX - 150) + "px")
            .style("top", (d3.event.pageY + 20) + "px");
    .on("mouseout", function () {
        return"opacity", "0");

var circleGroup ="g#circle-group");"button#wiggle").on("click", function() {
              "rotate("+ (20*(Math.random()-0.5)) + ")"
              +"translate(" + (20*(Math.random()-0.5)) +","
              + (20*(Math.random()-0.5)) + ")"