Wie kann man einen SVG-Text in Javascript unterbrechen?

107

Also hier ist was ich habe:

<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 ...
}
...

Jetzt hat mein sehr langer Tooltip-Text keinen Zeilenumbruch, obwohl ich alert () verwende. es zeigt mir, dass der Text tatsächlich zwei Zeilen hat. (Es enthält jedoch ein "\". Wie entferne ich dieses übrigens?)
Ich kann CDATA nirgendwo zum Laufen bringen.

sollniss
quelle
2
Gibt es eine Chance, dass dieser svgjs.com/textflow bei Ihrem Tooltip hilft?
Alvin K.
@AlvinK. Die Verbindung ist unterbrochen. Ich habe versucht, den neuen Standort zu finden, bin jedoch gescheitert.
Guettli

Antworten:

150

Dies wird von SVG 1.1 nicht unterstützt. SVG 1.2 verfügt zwar über das textAreaElement mit automatischem Zeilenumbruch, ist jedoch nicht in allen Browsern implementiert. SVG 2 plant keine ImplementierungtextArea , verfügt jedoch über automatisch umbrochenen Text .

Da Sie jedoch bereits wissen, wo Ihre Zeilenumbrüche auftreten sollten, können Sie Ihren Text in mehrere Zeilen <tspan>mit jeweils x="0"und dy="1.4em"zur Simulation tatsächlicher Textzeilen aufteilen. Beispielsweise:

<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>

Da Sie dies über JavaScript tun möchten, müssen Sie natürlich jedes Element manuell erstellen und in das DOM einfügen.

Sergiu Dumitriu
quelle
2
Und wie erkenne ich, wo ich das hinstellen soll <tspan>s? Ersetzen? Teilt?
Sollniss
2
Versucht es var tspan = document.createElement('tspan') tspan.setAttribute('x','0'); tspan.setAttribute('dy','1.2em'); tspan.textContent = text; tt.appendChild(tspan); zeigt überhaupt keinen Text.
Sollniss
2
Möchten Sie näher erläutern, warum x = '0' dy = '1.2em' benötigt wird? Es funktioniert tatsächlich genau so, wie Sie es gesagt haben. Ich hatte jedoch erwartet, dass es auch ohne diese Attribute funktionieren würde. Stattdessen wird nichts angezeigt ... Außerdem ist mir nicht ganz klar, warum der Zeilenumbruch überhaupt auftritt. Es ist nicht so, als hätten wir die Breite des Containers so eingestellt, dass etwas repariert wird, so dass Zeilenumbrüche auftreten können, oder?
Konrad Viltersten
4
x=0ist eine absolute Koordinate: Verschieben Sie das Textfragment an den Ursprung des aktuellen Koordinatensystems . Das transformAttribut für das gElement definiert ein neues aktuelles Koordinatensystem. Unter der Annahme, dass der Text linksbündig ist, wird der tspan nach links verschoben. Dies wirkt wie eine Wagenrücklaufanweisung. dy=1.2emist eine relative Koordinate: Verschieben Sie das Textfragment um diesen Betrag relativ zum aktuellen Textfragment. Dies wirkt wie eine Zeilenvorschubanweisung. Kombiniert erhalten Sie einen CR / LF.
Sergiu Dumitriu
Noch nicht ausprobiert: Könnten Sie dies auch ohne die Gruppe tun? <text x = "100" y = "100"> <tspan x = "100" y = "100"> sehr langer Text </ tspan> <tspan x = "100" y = "115"> möchte ich Zeilenumbruch </ tspan> </ text> ??
Richard
25

Ich nehme an, Sie haben es bereits geschafft, es zu lösen, aber wenn jemand nach einer ähnlichen Lösung sucht, dann hat dies für mich funktioniert:

 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; })

Es gibt 3 Zeilen, die durch Zeilenumbruch getrennt sind.

Kristīne Glode
quelle
21
FWIW: Es sieht so aus, als würde das OP reines JavaScript verwenden. Diese Antwort scheint D3 zu nutzen .
Ben Mosher
Ich benutze D3 und Ihr Ansatz hat bei mir funktioniert. Danke, dass du es gepostet hast. Ich stellte fest, dass ich zuerst die alten tspans löschen musste, bevor neue angehängt wurden, wie folgt: focus.selectAll ("tspan"). Remove ();
Darren Parker
1
Beachten Sie bei diesem Ansatz, dass die <tspan> -Tags verschachtelt werden, da .append () verkettet wird. Dies kann bei CSS zu leichten Kopfschmerzen führen, je nachdem, was Sie tun möchten.
Seneyr
Sehen Sie hier für einen Ansatz, der die Verschachtelung von @seneyr beschrieben vermeidet
bszom
16

Nehmen wir an, Sie wissen bei der tspan-Lösung nicht im Voraus, wo Sie Ihre Zeilenumbrüche platzieren sollen: Sie können diese nette Funktion verwenden, die ich hier gefunden habe: http://bl.ocks.org/mbostock/7555321

Dadurch werden automatisch Zeilenumbrüche für Langtext-SVG für eine bestimmte Breite in Pixel ausgeführt.

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);
      }
    }
  });
}
steco
quelle
9

Ich denke das macht was du willst:

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");
}

Es teilt den Text auf \\\nund fügt für jedes Fragment jedes Fragment in einen tspan ein. Anschließend wird die Größe des erforderlichen Felds anhand der längsten Textlänge und der Anzahl der Zeilen berechnet. Sie müssen auch das Tooltip-Textelement so ändern, dass es drei tspans enthält:

<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>

Dies setzt voraus, dass Sie nie mehr als drei Zeilen haben. Wenn Sie mehr als drei Zeilen möchten, können Sie weitere tspans hinzufügen und die Länge der for-Schleife erhöhen.

Peter Collingridge
quelle
Warum ist es "\\\n"eher als "\n"?
Ralien
2

Ich habe die Lösung von @steco ein wenig angepasst, die Abhängigkeit von d3auf umgeschaltet jqueryund das heightdes Textelements als Parameter hinzugefügt

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);
      }
    }
  });
}
loretoparisi
quelle
2

Verwenden Sie HTML anstelle von Javascript

<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>

Mila Nautikus
quelle
Ich denke du meinst "SVG anstelle von JavaScript verwenden"
Valerio Bozz
Es ist "HTML in SVG", die beste Lösung für mich!
Kévin Berthommier