append jquery tidak bekerja dengan elemen svg?

199

Dengan asumsi ini:

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

Kenapa aku tidak melihat apa-apa?


sumber

Jawaban:

249

Ketika Anda meneruskan string markup $, itu diuraikan sebagai HTML menggunakan innerHTMLproperti browser pada <div>(atau wadah lain yang cocok untuk kasus khusus seperti <tr>). innerHTMLtidak dapat mem-parsing SVG atau konten non-HTML lainnya, dan bahkan jika itu bisa, itu tidak akan dapat mengatakan bahwa <circle>itu seharusnya ada dalam namespace SVG.

innerHTMLtidak tersedia di SVGElement — ini hanya milik HTMLElement. Saat ini tidak ada innerSVGproperti atau cara lain (*) untuk mem-parsing konten ke dalam SVGElement. Untuk alasan ini, Anda harus menggunakan metode DOM-style. jQuery tidak memberi Anda akses mudah ke metode namespaced yang diperlukan untuk membuat elemen SVG. Sungguh jQuery tidak dirancang untuk digunakan dengan SVG sama sekali dan banyak operasi mungkin gagal.

HTML5 berjanji untuk membiarkan Anda menggunakannya <svg>tanpa dokumen xmlnsHTML ( text/html) biasa di masa mendatang. Tapi ini hanya hack parser (**), konten SVG masih akan menjadi SVGElements di namespace SVG, dan bukan HTMLElements, jadi Anda tidak akan dapat menggunakan innerHTMLmeskipun mereka terlihat seperti bagian dari dokumen HTML.

Namun, untuk peramban hari ini Anda harus menggunakan X HTML (berfungsi dengan baik application/xhtml+xml; simpan dengan ekstensi file .xhtml untuk pengujian lokal) agar SVG berfungsi sama sekali. (Ini agak masuk akal untuk tetap; SVG adalah standar berbasis XML dengan benar.) Ini berarti Anda harus melarikan diri <simbol di dalam blok skrip Anda (atau melampirkan di bagian CDATA), dan termasuk xmlnsdeklarasi XHTML . contoh:

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

*: well, ada parseWithContext DOM Level 3 LS , tetapi dukungan browser sangat buruk. Sunting untuk menambahkan: namun, sementara Anda tidak bisa menyuntikkan markup ke dalam SVGElement, Anda bisa menyuntikkan SVGElement baru ke dalam HTMLElement menggunakan innerHTML, lalu mentransfernya ke target yang diinginkan. Ini mungkin akan sedikit lebih lambat:

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

**: Saya benci cara penulis HTML5 tampaknya takut pada XML dan bertekad untuk menyuntikkan fitur berbasis XML ke dalam kekacauan crufty yaitu HTML. XHTML memecahkan masalah ini bertahun-tahun yang lalu.

bobince
sumber
7
Jawaban ini masih relevan! Saya baru saja mengalami bug aneh di mana elemen tambahan ditampilkan di inspektur elemen Chrome, tetapi tidak mau ditampilkan. Jika saya RMB> edit as htmlpada tag html dan tekan enter semuanya menampilkan (tetapi semua pendengar acara menghilang). Setelah membaca jawaban ini, saya mengubah panggilan createElement saya menjadi createElementNS dan sekarang semuanya berfungsi!
kitsu.eb
7
Anda bisa memanipulasi elemen SVG dengan jquery begitu elemen itu dibuat menggunakan metode DOM createElementNS. Anda dapat mengubah fungsi makeSVG menjadi 'mengembalikan $ (el)' dan sekarang Anda memiliki elemen svg yang dapat menggunakan metode jquery.
Hoffmann
6
Ah! Jadi itu sebabnya itu tidak akan berhasil. Saya mencoba hal yang sama persis dengan dua perpustakaan yang berbeda, satu di jQuery dan satu di D3.js . Saya mendapatkan output sumber yang sama persis di HTML menggunakan keduanya, tetapi elemen yang dihasilkan jQuery tidak akan membuat sementara yang dihasilkan D3 lakukan! Saya sarankan menggunakan D3:d3.select('body').append('svg').attr('width','100%');
chharvey
3
@MadsSkjern: DOM Level 3 LS tidak pernah berhasil masuk ke browser. DOMParser yang awalnya hanya Mozilla sekarang lebih banyak didukung, dan jQuery memilikinya $.parseXML.
bobince
1
Itu masih relevan. bobince membenci kekacauan HTML5, saya benci kekacauan seluruh web, karena tidak ada tempat yang dapat menemukan kode yang baik tanpa peretasan karena kekacauan itu. WWW harus diubah namanya menjadi WWM World Wide Mess.
Olivier Pons
149

The jawaban yang diterima menunjukkan terlalu rumit cara. Seperti yang Forresto klaim dalam jawabannya , " sepertinya menambahkan mereka di DOM explorer, tetapi tidak di layar " dan alasan untuk ini adalah ruang nama yang berbeda untuk html dan svg.

Solusi termudah adalah "menyegarkan" seluruh svg. Setelah menambahkan lingkaran (atau elemen lain), gunakan ini:

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

Ini caranya. Lingkaran ada di layar.

Atau jika Anda ingin, gunakan div wadah:

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

Dan bungkus svg Anda di dalam wadah:

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

Contoh fungsional:
http://jsbin.com/ejifab/1/edit

Keuntungan dari teknik ini:

  • Anda dapat mengedit svg yang ada (yang sudah ada di DOM), mis. dibuat menggunakan Raphael atau suka dalam contoh Anda "hard coded" tanpa skrip.
  • Anda dapat menambahkan struktur elemen kompleks seperti string misalnya. $('svg').prepend('<defs><marker></marker><mask></mask></defs>');seperti yang Anda lakukan di jQuery.
  • setelah elemen ditambahkan dan dibuat terlihat di layar menggunakan $("#cont").html($("#cont").html());atributnya dapat diedit menggunakan jQuery.

EDIT:

Teknik di atas berfungsi dengan "kode keras" atau DOM dimanipulasi (= document.createElementNS dll.) Hanya SVG. Jika Raphael digunakan untuk membuat elemen, (menurut tes saya) hubungan antara objek Raphael dan DOM SVG rusak jika $("#cont").html($("#cont").html());digunakan. Solusi untuk ini adalah tidak menggunakan $("#cont").html($("#cont").html());sama sekali dan alih-alih menggunakan dokumen dummy SVG.

Dummy SVG ini merupakan representasi tekstual dari dokumen SVG dan hanya berisi elemen yang diperlukan. Jika kita ingin mis. untuk menambahkan elemen filter ke dokumen Raphael, dummy bisa berupa sesuatu <svg id="dummy" style="display:none"><defs><filter><!-- Filter definitons --></filter></defs></svg>. Representasi tekstual pertama kali dikonversi ke DOM menggunakan metode $ ("body"). Append () jQuery. Dan ketika elemen (filter) ada di DOM, ia dapat ditanyakan menggunakan metode jQuery standar dan ditambahkan ke dokumen SVG utama yang dibuat oleh Raphael.

Mengapa boneka ini dibutuhkan? Mengapa tidak menambahkan elemen filter secara ketat ke dokumen yang dibuat Raphael? Jika Anda mencobanya menggunakan mis. $("svg").append("<circle ... />"), itu dibuat sebagai elemen html dan tidak ada yang ada di layar seperti yang dijelaskan dalam jawaban. Tetapi jika seluruh dokumen SVG ditambahkan, maka browser menangani secara otomatis konversi namespace dari semua elemen dalam dokumen SVG.

Contoh mencerahkan teknik:

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

Demo kerja penuh dari teknik ini ada di sini: http://jsbin.com/ilinan/1/edit .

(Saya (belum) tidak tahu, mengapa $("#cont").html($("#cont").html());tidak bekerja saat menggunakan Raphael. Ini akan menjadi retasan yang sangat singkat.)

Timo Kähkönen
sumber
Saya menggunakan solusi (buatan rumah) saya untuk menangani SVG dan memiliki masalah yang sama dengan Raphael, ketika menggunakan "trik reparasi" dengan $ (# picture) .html ..., tetapi solusi saya adalah memasukkan kembali beberapa SVG yang di-cache elemen (menandai persegi panjang, transformasi dan sebagainya)
halfbit
Anda seorang penyihir. Saya melihat jawaban yang diterima dan saya seperti manusia, ini akan menyebalkan. Kemudian, saya melihat ini dan melakukan semua yang saya butuhkan dalam satu baris pendek. Terima kasih!
The Composer
1
INI ADALAH MENYEBABKAN SAYA UNTUK KEHILANGAN MARKEL SAYA ... pikir saya secara ajaib bisa menggunakan js DOM elms di namespace SVG seperti yang mungkin berpikir ... dapatkan inspektur tag untuk mengenali sisipan ... tetapi tidak dadu sampai sekarang!
Gus Crawford
Bekerja sangat bagus untuk saya ketika menambahkan tag poligon dan path saya yang ada ke tag svg induk yang baru dibuat. Terima kasih!
Kivak Wolf
1
$("#cont").html($("#cont").html());bekerja dengan baik di Chrome, tetapi tidak berhasil di IE 11 untuk saya.
CoderDennis
37

Pustaka D3 yang semakin populer menangani keanehan menambahkan / memanipulasi svg dengan sangat baik. Anda mungkin ingin mempertimbangkan untuk menggunakannya sebagai lawan dari peretasan jQuery yang disebutkan di sini.

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");
baik
sumber
jawaban bagus. itu membantu saya untuk beberapa masalah di sini.
Ismail baig
jQuery juga tersedak pengaturan / mendapatkan kelas dari SVG juga. Sebelah samping.
QueueHammer
24

JQuery tidak dapat menambahkan elemen ke <svg>(sepertinya menambahkannya di DOM explorer, tetapi tidak di layar).

Salah satu solusinya adalah menambahkan <svg>dengan semua elemen yang Anda butuhkan ke halaman, dan kemudian memodifikasi atribut dari elemen yang digunakan .attr().

$('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/

forresto
sumber
Terima kasih, banyak membantu saya! Ini adalah pendekatan yang sangat bagus. Alih-alih menambahkan <circle>atau elemen lain secara individual menggunakan metode DOM yang kompleks dan lambat (misalnya. createDocumentFragment()Atau createElementNS()), tambahkan seluruh dokumen svg ke dalam wadah. Alih-alih $ ('tubuh') tentu saja dapat beberapa $ ('div') juga. Dan setelah itu dokumen svg ada di DOM dan dapat di-query dengan cara yang umum menggunakan mis. $ ('c'). attr ('fill', 'red').
Timo Kähkönen
Saya membuat contoh tambahan tentang ini: jsbin.com/inevaz/1 . Ia mendapatkan kode sumber tiger.svg dari internet ke iframe dan dapat disalin-ditempelkan ke textarea. Konten textarea kemudian digunakan sebagai sumber untuk inline svg yang kemudian dapat diakses melalui DOM (untuk mengubah gaya goresan).
Timo Kähkönen
Ini bagus, saya tidak tahu mengapa semua orang memilih jawaban lain yang tidak berfungsi.
Dustin Poissant
Ini juga jawaban terbaik bagi saya. Saya membutuhkan svg dengan <use xlink: href = "# icon"> dan ini adalah jawaban yang paling singkat.
Eydamos
17

Saya belum pernah melihat seseorang yang menyebutkan metode ini tetapi document.createElementNS()sangat membantu dalam hal ini.

Anda dapat membuat elemen menggunakan Javascript vanilla sebagai node DOM normal dengan namespace yang benar dan kemudian jQuery-ify mereka dari sana. Seperti itu:

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

Satu-satunya sisi negatifnya adalah Anda harus membuat setiap elemen SVG dengan namespace yang tepat secara individual atau tidak akan berfungsi.

Chris Dolphin
sumber
2
Jawaban Timo (pilihan teratas) bekerja di Chrome, tetapi tidak untuk IE. Jawaban ini memecahkan masalah untuk kedua browser!
CoderDennis
1
Bagus! Bagus untuk tahu
Chris Dolphin
14

Menemukan cara mudah yang berfungsi dengan semua browser yang saya miliki (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>

Matthieu Charbonnier
sumber
5
Baris penyegaran itu persis seperti yang saya cari. Browser yang konyol. Terima kasih!
Sam Soffes
8

Saya dapat melihat lingkaran di firefox, melakukan 2 hal:

1) Mengganti nama file dari html ke xhtml

2) Ubah skrip menjadi

<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>
Topera
sumber
1
Delapan tahun kemudian dan ini masih membantu!
Don 01001100
5

Berdasarkan jawaban @ chris-dolphin tetapi menggunakan fungsi helper:

// 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);
Jonas Berlin
sumber
1
Anda tentu saja dapat juga melakukan:$svg.append($s('<circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red"/>'));
Jonas Berlin
4

Jika string yang Anda perlu tambahkan adalah SVG dan Anda menambahkan namespace yang tepat, Anda dapat menguraikan string sebagai XML dan menambahkan ke induk.

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))
jdesjean
sumber
3

Jawaban yang diterima oleh Bobince adalah solusi singkat dan portabel. Jika Anda tidak hanya perlu menambahkan SVG tetapi juga memanipulasi, Anda bisa mencoba perpustakaan JavaScript "Pablo" (saya menulisnya). Ini akan terasa akrab bagi pengguna jQuery.

Contoh kode Anda kemudian akan terlihat seperti:

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

Anda juga dapat membuat elemen SVG dengan cepat, tanpa menentukan markup:

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

Saya akan menyarankan mungkin lebih baik menggunakan ajax dan memuat elemen svg dari halaman lain.

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

Di mana href adalah lokasi halaman dengan svg. Dengan cara ini Anda dapat menghindari efek gelisah yang mungkin terjadi karena mengganti konten html. Juga, jangan lupa membuka bungkusan svg setelah dimuat:

$('.svg_element').unwrap();
Dan185
sumber
0

Ini bekerja untuk saya hari ini dengan 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");
}

Membuat:

gambar SVG ini seperti yang terlihat di Firefox 57

paul_h
sumber
Itu bukan elemen SVG yang Anda tambahkan, itu konten teks.
Robert Longson
Itu mungkin, tapi itu diberikan setelah memuat halaman awal SVG inline statis.
paul_h
Jadi apa, itu bukan pertanyaan yang diajukan, yaitu tentang elemen svg dan bukan konten teks.
Robert Longson
$(this).clone()mengkloning elemen SVG (dan itu anak-anak jika ada). Lihatlah melewati penggunaan text(). Saya mengambil satu tspan yang memiliki "Your New" di dalamnya, dan berakhir dengan dua tspan, satu dengan "Your" di dalamnya (hitam) dan satu dengan "New in it" (w / line-through biru). Astaga, turun dari 0 suara ke -1 :-(
paul_h
0
 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.
4baad4
sumber
-1

Cara yang jauh lebih sederhana adalah dengan hanya menghasilkan SVG Anda menjadi sebuah string, membuat elemen HTML pembungkus dan memasukkan string svg ke dalam elemen HTML menggunakan $("#wrapperElement").html(svgString). Ini berfungsi dengan baik di Chrome dan Firefox.

Jakob Jenkov
sumber