[javascript] How to linebreak an svg text within javascript?

So here is what I have:

<path class="..." onmousemove="show_tooltip(event,'very long text 
    \\\n I would like to linebreak')" onmouseout="hide_tooltip()" d="..."/>

<rect class="tooltip_bg" id="tooltip_bg" ... />
<text class="tooltip" id="tooltip" ...>Tooltip</text>

<script>
<![CDATA[
function show_tooltip(e,text) {
    var tt = document.getElementById('tooltip');
    var bg = document.getElementById('tooltip_bg');

    // set position ...

    tt.textContent=text;

    bg.setAttribute('width',tt.getBBox().width+10);
    bg.setAttribute('height',tt.getBBox().height+6);

    // set visibility ...
}
...

Now my very long tooltip text doesn't have a linebreak, even though if I use alert(); it shows me that the text actually DOES have two lines. (It contains a "\" though, how do I remove that one by the way?)
I can't get CDATA to work anywhere.

This question is related to javascript svg newline line-breaks

The answer is


With the tspan solution, let's say you don't know in advance where to put your line breaks: you can use this nice function, that I found here: http://bl.ocks.org/mbostock/7555321

That automatically does line breaks for long text svg for a given width in pixel.

function wrap(text, width) {
  text.each(function() {
    var text = d3.select(this),
        words = text.text().split(/\s+/).reverse(),
        word,
        line = [],
        lineNumber = 0,
        lineHeight = 1.1, // ems
        y = text.attr("y"),
        dy = parseFloat(text.attr("dy")),
        tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em");
    while (word = words.pop()) {
      line.push(word);
      tspan.text(line.join(" "));
      if (tspan.node().getComputedTextLength() > width) {
        line.pop();
        tspan.text(line.join(" "));
        line = [word];
        tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
      }
    }
  });
}

I suppese you alredy managed to solve it, but if someone is looking for similar solution then this worked for me:

 g.append('svg:text')
  .attr('x', 0)
  .attr('y', 30)
  .attr('class', 'id')
  .append('svg:tspan')
  .attr('x', 0)
  .attr('dy', 5)
  .text(function(d) { return d.name; })
  .append('svg:tspan')
  .attr('x', 0)
  .attr('dy', 20)
  .text(function(d) { return d.sname; })
  .append('svg:tspan')
  .attr('x', 0)
  .attr('dy', 20)
  .text(function(d) { return d.idcode; })

There are 3 lines separated with linebreak.


use HTML instead of javascript

_x000D_
_x000D_
<html>_x000D_
  <head><style> * { margin: 0; padding: 0; } </style></head>_x000D_
  <body>_x000D_
    <h1>svg foreignObject to embed html</h1>_x000D_
_x000D_
    <svg_x000D_
      xmlns="http://www.w3.org/2000/svg"_x000D_
      viewBox="0 0 300 300"_x000D_
      x="0" y="0" height="300" width="300"_x000D_
    >_x000D_
_x000D_
      <circle_x000D_
        r="142" cx="150" cy="150"_x000D_
        fill="none" stroke="#000000" stroke-width="2"_x000D_
      />_x000D_
_x000D_
      <foreignObject_x000D_
        x="50" y="50" width="200" height="200"_x000D_
      >_x000D_
        <div_x000D_
          xmlns="http://www.w3.org/1999/xhtml"_x000D_
          style="_x000D_
            width: 196px; height: 196px;_x000D_
            border: solid 2px #000000;_x000D_
            font-size: 32px;_x000D_
            overflow: auto; /* scroll */_x000D_
          "_x000D_
        >_x000D_
          <p>this is html in svg 1</p>_x000D_
          <p>this is html in svg 2</p>_x000D_
          <p>this is html in svg 3</p>_x000D_
          <p>this is html in svg 4</p>_x000D_
        </div>_x000D_
      </foreignObject>_x000D_
_x000D_
    </svg>_x000D_
_x000D_
</body></html>
_x000D_
_x000D_
_x000D_


I think this does what you want:

function ShowTooltip(evt, mouseovertext){
    // Make tooltip text        
    var tooltip_text = tt.childNodes.item(1);
    var words = mouseovertext.split("\\\n");
    var max_length = 0;

    for (var i=0; i<3; i++){
        tooltip_text.childNodes.item(i).firstChild.data = i<words.length ?  words[i] : " ";
        length = tooltip_text.childNodes.item(i).getComputedTextLength();
        if (length > max_length) {max_length = length;}
    }

    var x = evt.clientX + 14 + max_length/2;
    var y = evt.clientY + 29;
    tt.setAttributeNS(null,"transform", "translate(" + x + " " + y + ")")

    // Make tooltip background
    bg.setAttributeNS(null,"width", max_length+15);
    bg.setAttributeNS(null,"height", words.length*15+6);
    bg.setAttributeNS(null,"x",evt.clientX+8);
    bg.setAttributeNS(null,"y",evt.clientY+14);

    // Show everything
    tt.setAttributeNS(null,"visibility","visible");
    bg.setAttributeNS(null,"visibility","visible");
}

It splits the text on \\\n and for each puts each fragment in a tspan. Then it calculates the size of the box required based on the longest length of text and the number of lines. You will also need to change the tooltip text element to contain three tspans:

<g id="tooltip" visibility="hidden">
    <text><tspan>x</tspan><tspan x="0" dy="15">x</tspan><tspan x="0" dy="15">x</tspan></text>
</g>

This assumes that you never have more than three lines. If you want more than three lines you can add more tspans and increase the length of the for loop.


I have adapted a bit the solution by @steco, switching the dependency from d3 to jquery and adding the height of the text element as parameter

function wrap(text, width, height) {
  text.each(function(idx,elem) {
    var text = $(elem);
    text.attr("dy",height);
        var words = text.text().split(/\s+/).reverse(),
        word,
        line = [],
        lineNumber = 0,
        lineHeight = 1.1, // ems
        y = text.attr("y"),
        dy = parseFloat( text.attr("dy") ),
        tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em");
    while (word = words.pop()) {
      line.push(word);
      tspan.text(line.join(" "));
      if (elem.getComputedTextLength() > width) {
        line.pop();
        tspan.text(line.join(" "));
        line = [word];
        tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
      }
    }
  });
}

Examples related to javascript

need to add a class to an element How to make a variable accessible outside a function? Hide Signs that Meteor.js was Used How to create a showdown.js markdown extension Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Summing radio input values How to execute an action before close metro app WinJS javascript, for loop defines a dynamic variable name Getting all files in directory with ajax

Examples related to svg

How to extract svg as file from web page How to display svg icons(.svg files) in UI using React Component? Why don’t my SVG images scale using the CSS "width" property? How to show SVG file on React Native? Easiest way to use SVG in Android? IE11 meta element Breaks SVG img src SVG changing the styles with CSS How to use SVG markers in Google Maps API v3 Embedding SVG into ReactJS How to change the color of an svg element?

Examples related to newline

How can I insert a line break into a <Text> component in React Native? Print "\n" or newline characters as part of the output on terminal Using tr to replace newline with space How to write one new line in Bitbucket markdown? Line break in SSRS expression How to insert a new line in Linux shell script? Replace CRLF using powershell How to write new line character to a file in Java What is the newline character in the C language: \r or \n? How to print values separated by spaces instead of new lines in Python 2.7

Examples related to line-breaks

HTML5 tag for horizontal line break Split string in JavaScript and detect line break Match linebreaks - \n or \r\n? How to linebreak an svg text within javascript? What is the difference between a "line feed" and a "carriage return"? Bash: Strip trailing linebreak from output How to read a file without newlines? How to break lines in PowerShell? How do I specify new lines on Python, when writing on files? How to remove line breaks (no characters!) from the string?