webentwicklung-frage-antwort-db.com.de

jquery append funktioniert nicht mit svg element?

Angenommen, dies:

<html>
<head>
 <script type="text/javascript" src="jquery.js"></script>
 <script type="text/javascript">
 $(document).ready(function(){
  $("svg").append('<circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red"/>');
 });
 </script>
</head>
<body>
 <svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 100" width="200px" height="100px">
 </svg>
</body>

Warum sehe ich nichts?

192
1521237

Wenn Sie einen Markup-String an $ Übergeben, wird dieser mit der Eigenschaft innerHTML des Browsers in einem <div> (Oder einem anderen geeigneten Container für Sonderfälle wie <tr> Als HTML analysiert. ]). innerHTML kann SVG- oder andere Nicht-HTML-Inhalte nicht analysieren, und selbst wenn dies möglich wäre, könnte nicht festgestellt werden, dass <circle> im SVG-Namespace enthalten sein sollte.

innerHTML ist in SVGElement nicht verfügbar - es ist nur eine Eigenschaft von HTMLElement. Weder gibt es derzeit eine innerSVG -Eigenschaft noch eine andere Möglichkeit (*) zum Parsen von Inhalten in ein SVGElement. Aus diesem Grund sollten Sie Methoden im DOM-Stil verwenden. Mit jQuery haben Sie keinen einfachen Zugriff auf die Namespace-Methoden, die zum Erstellen von SVG-Elementen erforderlich sind. Wirklich, jQuery ist überhaupt nicht für die Verwendung mit SVG ausgelegt, und viele Vorgänge schlagen möglicherweise fehl.

HTML5 verspricht, dass Sie in Zukunft <svg> Ohne xmlns in einem einfachen HTML-Dokument (text/html) Verwenden können. Dies ist jedoch nur ein Parser-Hack (**). Der SVG-Inhalt ist weiterhin SVGElements im SVG-Namespace und nicht HTMLElements, sodass Sie innerHTML nicht verwenden können, obwohl sie sieht aus wie ein Teil eines HTML-Dokuments.

Für heutige Browser müssen Sie jedoch [~ # ~] x [~ # ~] HTML (ordnungsgemäß als application/xhtml+xml Bereitgestellt) speichern mit der Dateierweiterung .xhtml für lokale Tests), damit SVG überhaupt funktioniert. (Es macht sowieso Sinn; SVG ist ein ordnungsgemäß XML-basierter Standard.) Dies bedeutet, dass Sie die < - Symbole in Ihrem Skriptblock umgehen (oder in einen CDATA-Abschnitt einschließen) und einschließen müssen die XHTML xmlns -Deklaration. Beispiel:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"><head>
</head><body>
    <svg id="s" xmlns="http://www.w3.org/2000/svg"/>
    <script type="text/javascript">
        function makeSVG(tag, attrs) {
            var el= document.createElementNS('http://www.w3.org/2000/svg', tag);
            for (var k in attrs)
                el.setAttribute(k, attrs[k]);
            return el;
        }

        var circle= makeSVG('circle', {cx: 100, cy: 50, r:40, stroke: 'black', 'stroke-width': 2, fill: 'red'});
        document.getElementById('s').appendChild(circle);
        circle.onmousedown= function() {
            alert('hello');
        };
    </script>
</body></html>

*: Nun, es gibt DOM Level 3 LSs parseWithContext , aber die Browserunterstützung ist sehr schlecht. Bearbeiten zum Hinzufügen: Sie können zwar kein Markup in ein SVGElement einfügen, aber Sie können mit innerHTML ein neues SVGElement in ein HTMLElement einfügen und es dann auf das gewünschte Ziel übertragen. Es wird wahrscheinlich etwas langsamer sein:

<script type="text/javascript"><![CDATA[
    function parseSVG(s) {
        var div= document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
        div.innerHTML= '<svg xmlns="http://www.w3.org/2000/svg">'+s+'</svg>';
        var frag= document.createDocumentFragment();
        while (div.firstChild.firstChild)
            frag.appendChild(div.firstChild.firstChild);
        return frag;
    }

    document.getElementById('s').appendChild(parseSVG(
        '<circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red" onmousedown="alert(\'hello\');"/>'
    ));
]]></script>

**: Ich hasse die Art und Weise, wie die Autoren von HTML5 Angst vor XML zu haben scheinen und entschlossen sind, XML-basierte Funktionen in das schmutzige Chaos von HTML zu stecken. XHTML hat diese Probleme vor Jahren gelöst.

237
bobince

Die akzeptierte Antwort zeigt einen zu komplizierten Weg. Wie Forresto in seiner Antwort behauptet, " es scheint, dass sie im DOM Explorer hinzugefügt werden, aber nicht auf dem Bildschirm" und der Grund dafür sind unterschiedliche Namespaces für html und svg.

Die einfachste Problemumgehung besteht darin, das gesamte SVG zu "aktualisieren". Verwenden Sie nach dem Anhängen eines Kreises (oder anderer Elemente) Folgendes:

$("body").html($("body").html());

Das macht den Trick. Der Kreis befindet sich auf dem Bildschirm.

Oder wenn Sie möchten, verwenden Sie einen Container div:

$("#cont").html($("#cont").html());

Und wickeln Sie Ihre Svg in Container div:

<div id="cont">
    <svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 100" width="200px" height="100px">
    </svg>
</div>

Das Funktionsbeispiel:
http://jsbin.com/ejifab/1/edit

Die Vorteile dieser Technik:

  • sie können vorhandene svg bearbeiten (das ist bereits in DOM), z. erstellt mit Raphael oder ähnlichem in deinem Beispiel "hard coded" ohne scripting.
  • sie können komplexe Elementstrukturen als Zeichenfolgen hinzufügen, z. $('svg').prepend('<defs><marker></marker><mask></mask></defs>'); wie in jQuery.
  • nachdem die Elemente mit $("#cont").html($("#cont").html()); angehängt und auf dem Bildschirm sichtbar gemacht wurden, können ihre Attribute mit jQuery bearbeitet werden.

BEARBEITEN:

Die obige Technik funktioniert nur mit "hartcodiertem" oder DOM-manipuliertem (= document.createElementNS usw.) SVG. Wenn Raphael zum Erstellen von Elementen verwendet wird, wird (nach meinen Tests) die Verknüpfung zwischen Raphael-Objekten und SVG-DOM unterbrochen, wenn $("#cont").html($("#cont").html()); verwendet wird. Die Problemumgehung besteht darin, $("#cont").html($("#cont").html()); überhaupt nicht zu verwenden und stattdessen ein SVG-Dummy-Dokument zu verwenden.

Diese Dummy-SVG ist zunächst eine Textdarstellung des SVG-Dokuments und enthält nur die Elemente, die benötigt werden. Wenn wir wollen, z. Um ein Filterelement in ein Raphael-Dokument einzufügen, könnte der Dummy so etwas wie <svg id="dummy" style="display:none"><defs><filter><!-- Filter definitons --></filter></defs></svg> sein. Die Textdarstellung wird zuerst mit der Methode $ ("body"). Append () von jQuery in DOM konvertiert. Wenn sich das (Filter-) Element in DOM befindet, kann es mit Standard-jQuery-Methoden abgefragt und an das von Raphael erstellte SVG-Hauptdokument angehängt werden.

Warum wird dieser Dummy benötigt? Warum nicht ein Filterelement strikt zu einem von Raphael erstellten Dokument hinzufügen? Wenn Sie es mit zB versuchen. $("svg").append("<circle ... />"), es wird als HTML-Element erstellt und nichts wird auf dem Bildschirm angezeigt, wie in den Antworten beschrieben. Wird jedoch das gesamte SVG-Dokument angehängt, übernimmt der Browser automatisch die Namespace-Konvertierung aller Elemente im SVG-Dokument.

Ein Beispiel verdeutlicht die Technik:

// Add Raphael SVG document to container element
var p = Raphael("cont", 200, 200);
// Add id for easy access
$(p.canvas).attr("id","p");
// Textual representation of element(s) to be added
var f = '<filter id="myfilter"><!-- filter definitions --></filter>';

// Create dummy svg with filter definition 
$("body").append('<svg id="dummy" style="display:none"><defs>' + f + '</defs></svg>');
// Append filter definition to Raphael created svg
$("#p defs").append($("#dummy filter"));
// Remove dummy
$("#dummy").remove();

// Now we can create Raphael objects and add filters to them:
var r = p.rect(10,10,100,100);
$(r.node).attr("filter","url(#myfilter)");

Die vollständige Demo dieser Technik finden Sie hier: http://jsbin.com/ilinan/1/edit .

(Ich habe (noch) keine Ahnung, warum $("#cont").html($("#cont").html()); mit Raphael nicht funktioniert. Es wäre ein sehr kurzer Hack.)

143
Timo Kähkönen

Die immer beliebter werdende D Bibliothek behandelt die Besonderheiten des Anhängens/Manipulierens von svg sehr gut. Möglicherweise möchten Sie es im Gegensatz zu den hier genannten jQuery-Hacks verwenden.

HTML

<svg xmlns="http://www.w3.org/2000/svg"></svg>

Javascript

var circle = d3.select("svg").append("circle")
    .attr("r", "10")
    .attr("style", "fill:white;stroke:black;stroke-width:5");
36
nategood

JQuery kann keine Elemente an <svg> Anhängen (es scheint, dass sie im DOM-Explorer hinzugefügt werden, aber nicht auf dem Bildschirm).

Eine Problemumgehung besteht darin, ein <svg> Mit allen Elementen, die Sie benötigen, an die Seite anzuhängen und dann die Attribute der Elemente mit .attr() zu ändern.

$('body')
  .append($('<svg><circle id="c" cx="10" cy="10" r="10" fill="green" /></svg>'))
  .mousemove( function (e) {
      $("#c").attr({
          cx: e.pageX,
          cy: e.pageY
      });
  });

http://jsfiddle.net/8FBjb/1/

25
forresto

Ich habe noch niemanden gesehen, der diese Methode erwähnt hat, aber document.createElementNS() ist in diesem Fall hilfreich.

Sie können die Elemente mit Vanilla Javascript als normale DOM-Knoten mit dem richtigen Namespace erstellen und sie dann von dort aus jQuery-ify. Wie so:

var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'),
    circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');

var $circle = $(circle).attr({ //All your attributes });

$(svg).append($circle);

Der einzige Nachteil ist, dass Sie jedes SVG-Element mit dem richtigen Namespace einzeln erstellen müssen, sonst funktioniert es nicht.

14
Chris Dolphin

Es wurde ein einfacher Weg gefunden, der mit allen meinen Browsern funktioniert (Chrome 49, Edge 25, Firefox 44, IE11, Safari 5 [Win], Safari 8 (MacOS)):

// Clean svg content (if you want to update the svg's objects)
// Note : .html('') doesn't works for svg in some browsers
$('#svgObject').empty();
// add some objects
$('#svgObject').append('<polygon class="svgStyle" points="10,10 50,10 50,50 10,50 10,10" />');
$('#svgObject').append('<circle class="svgStyle" cx="100" cy="30" r="25"/>');

// Magic happens here: refresh DOM (you must refresh svg's parent for Edge/IE and Safari)
$('#svgContainer').html($('#svgContainer').html());
.svgStyle
{
  fill:cornflowerblue;
  fill-opacity:0.2;
  stroke-width:2;
  stroke-dasharray:5,5;
  stroke:black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="svgContainer">
  <svg id="svgObject" height="100" width="200"></svg>
</div>

<span>It works if two shapes (one square and one circle) are displayed above.</span>
10

Ich kann in Firefox einen Kreis sehen, der zwei Dinge tut:

1) Umbenennen der Datei von html nach xhtml

2) Ändern Sie das Skript in

<script type="text/javascript">
$(document).ready(function(){
    var obj = document.createElementNS("http://www.w3.org/2000/svg", "circle");
    obj.setAttributeNS(null, "cx", 100);
    obj.setAttributeNS(null, "cy", 50);
    obj.setAttributeNS(null, "r",  40);
    obj.setAttributeNS(null, "stroke", "black");
    obj.setAttributeNS(null, "stroke-width", 2);
    obj.setAttributeNS(null, "fill", "red");
    $("svg")[0].appendChild(obj);
});
</script>
8
Topera

Basierend auf der Antwort von @ chris-dolphin, aber unter Verwendung der Hilfsfunktion:

// Creates svg element, returned as jQuery object
function $s(elem) {
  return $(document.createElementNS('http://www.w3.org/2000/svg', elem));
}

var $svg = $s("svg");
var $circle = $s("circle").attr({...});
$svg.append($circle);
5
Jonas Berlin

Wenn die anzuhängende Zeichenfolge SVG ist und Sie den richtigen Namespace hinzufügen, können Sie die Zeichenfolge als XML analysieren und an die übergeordnete Zeichenfolge anhängen.

var xml = jQuery.parseXML('<circle xmlns="http://www.w3.org/2000/svg" cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red"/>');
$("svg").append(xml.documentElement))
4
jdesjean

Die von Bobince akzeptierte Antwort ist eine kurze, tragbare Lösung. Wenn Sie SVG nicht nur anhängen, sondern auch bearbeiten müssen, können Sie es mit JavaScript-Bibliothek "Pablo" (Ich habe es geschrieben) versuchen. JQuery-Benutzer werden sich damit vertraut fühlen.

Ihr Codebeispiel würde dann so aussehen:

$(document).ready(function(){
    Pablo("svg").append('<circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red"/>');
});

Sie können SVG-Elemente auch direkt ohne Angabe von Markup erstellen:

var circle = Pablo.circle({
    cx:100,
    cy:50,
    r:40
}).appendTo('svg');
3
Premasagar

Ich würde vorschlagen, dass es besser ist, Ajax zu verwenden und das SVG-Element von einer anderen Seite zu laden.

$('.container').load(href + ' .svg_element');

Wobei href der Speicherort der Seite mit dem SVG ist. Auf diese Weise können Sie nervöse Effekte vermeiden, die beim Ersetzen des HTML-Inhalts auftreten können. Vergessen Sie auch nicht, das SVG nach dem Laden zu entpacken:

$('.svg_element').unwrap();
1
Dan185

Das funktioniert für mich heute mit FF 57:

function () {
    // JQuery, today, doesn't play well with adding SVG elements - tricks required
    $(selector_to_node_in_svg_doc).parent().prepend($(this).clone().text("Your"));
    $(selector_to_node_in_svg_doc).text("New").attr("x", "340").text("New")
        .attr('stroke', 'blue').attr("style", "text-decoration: line-through");
}

Macht:

this SVG image as seen in Firefox 57

0
paul_h
 var svg; // if you have variable declared and not assigned value.
 // then you make a mistake by appending elements to that before creating element    
 svg.appendChild(document.createElement("g"));
 // at some point you assign to svg
 svg = document.createElementNS('http://www.w3.org/2000/svg', "svg")
 // then you put it in DOM
 document.getElementById("myDiv").appendChild(svg)
 // it wont render unless you manually change myDiv DOM with DevTools

// to fix assign before you append
var svg = createElement("svg", [
    ["version", "1.2"],
    ["xmlns:xlink", "http://www.w3.org/1999/xlink"],
    ["aria-labelledby", "title"],
    ["role", "img"],
    ["class", "graph"]
]);
function createElement(tag, attributeArr) {
      // .createElementNS  NS is must! Does not draw without
      let elem = document.createElementNS('http://www.w3.org/2000/svg', tag);             
      attributeArr.forEach(element => elem.setAttribute(element[0], element[1]));
      return elem;
}
// extra: <circle> for example requires attributes to render. Check if missing.
0
4baad4