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.
To create a line break in JavaScript, use “<br>”. With this, we can add more than one line break also.
How to Do a Line Break in HTML. To do a line break in HTML, use the <br> tag.
SVG 1.2 introduces editable text fields, moving the burden of text input and editing to the user agent, which has access to system text libraries.
This is not something that SVG 1.1 supports. SVG 1.2 does have the textArea
element, with automatic word wrapping, but it's not implemented in all browsers. SVG 2 does not plan on implementing textArea
, but it does have auto-wrapped text.
However, given that you already know where your linebreaks should occur, you can break your text into multiple <tspan>
s, each with x="0"
and dy="1.4em"
to simulate actual lines of text. For example:
<g transform="translate(123 456)"><!-- replace with your target upper left corner coordinates -->
<text x="0" y="0">
<tspan x="0" dy="1.2em">very long text</tspan>
<tspan x="0" dy="1.2em">I would like to linebreak</tspan>
</text>
</g>
Of course, since you want to do that from JavaScript, you'll have to manually create and insert each element into the DOM.
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.
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")) || 0,
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);
}
}
});
}
use HTML instead of javascript
limitation: the SVG renderer must support HTML rendering
for example, inkscape cannot render such SVG files
<html>
<head><style> * { margin: 0; padding: 0; } </style></head>
<body>
<h1>svg foreignObject to embed html</h1>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 300 300"
x="0" y="0" height="300" width="300"
>
<circle
r="142" cx="150" cy="150"
fill="none" stroke="#000000" stroke-width="2"
/>
<foreignObject
x="50" y="50" width="200" height="200"
>
<div
xmlns="http://www.w3.org/1999/xhtml"
style="
width: 196px; height: 196px;
border: solid 2px #000000;
font-size: 32px;
overflow: auto; /* scroll */
"
>
<p>this is html in svg 1</p>
<p>this is html in svg 2</p>
<p>this is html in svg 3</p>
<p>this is html in svg 4</p>
</div>
</foreignObject>
</svg>
</body></html>
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);
}
}
});
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With