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
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
<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_
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);
}
}
});
}
Source: Stackoverflow.com