jquery's append svg öğesi ile çalışmıyor?


199

Bunu varsayarak:

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

Neden hiçbir şey görmüyorum?

jquery  html  svg 

Yanıtlar:


249

Bir biçimlendirme dizesini $ilettiğinizde, (veya özel durumlar için uygun başka bir kapta) tarayıcının innerHTMLözelliği kullanılarak HTML olarak ayrıştırılır . SVG'yi veya HTML olmayan diğer içerikleri ayrıştıramıyor ve mümkün olsa bile bunun SVG ad alanında olması gerektiğini söyleyemedi .<div><tr>innerHTML<circle>

innerHTMLSVGElement'te mevcut değildir — yalnızca HTMLElement'in bir özelliğidir. Şu anda innerSVGiçeriklerin bir SVGElement'e ayrıştırılması için bir özellik veya başka bir yol (*) da yoktur. Bu nedenle DOM tarzı yöntemler kullanmalısınız. jQuery, SVG öğeleri oluşturmak için gereken ad boşluklu yöntemlere kolay erişim sağlamaz. Gerçekten jQuery, SVG ile kullanılmak üzere tasarlanmamıştır ve birçok işlem başarısız olabilir.

HTML5 , gelecekte düz bir HTML ( ) belgesi <svg>olmadan kullanmanıza izin verir . Ama bu kullanmak mümkün olmayacaktır olacak, böylece SVG içerik yine SVG ad alanındaki SVGElements değil, HTMLElements olacaktır (**) sadece bir ayrıştırıcı kesmek onlar olsa bile bakmak bir HTML belgesinin bir parçası gibi.xmlnstext/htmlinnerHTML

Ancak, bugünün tarayıcıları için SVG'nin çalışabilmesi için X HTML (düzgün şekilde application/xhtml+xml; yerel test için .xhtml dosya uzantısıyla kaydedin) kullanmanız gerekir. (Zaten mantıklıdır; SVG düzgün bir XML tabanlı standarttır.) Bu <, komut dosyası bloğunuzdaki sembollerden kaçmanız (veya bir CDATA bölümüne dahil etmeniz) ve XHTML xmlnsbildirimini dahil etmeniz gerektiği anlamına gelir . misal:

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

*: Evet, DOM Level 3 LS'nin parseWithContext'i var , ancak tarayıcı desteği çok zayıf. Eklemek için düzenleyin: Bununla birlikte, bir SVGElement'e işaretleme enjekte edemeseniz de, kullanarak HTMLElement'e yeni bir SVGElement enjekte edebilir ve innerHTMLardından istediğiniz hedefe aktarabilirsiniz. Yine de muhtemelen biraz daha yavaş olacak:

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

**: HTML5 yazarlarının XML'den korkuyor gibi görünmesinden ve XML tabanlı özellikleri HTML olan acımasız karmaşaya sokmaya kararlıyım. XHTML yıllar önce bu sorunları çözdü.


7
Bu cevap hala geçerli! Chrome öğesi denetçisinde eklenen öğelerin gösterildiği, ancak oluşturulmadığı garip bir hata yaşadım. Ben ise RMB> edit as htmlhtml etiketi ve isabet girmek herşeyi görüntüler (ancak tüm olay dinleyicileri kaybolur). Bu yanıtı okuduktan sonra createElementNS için createElement çağrılarımı değiştirdim ve şimdi her şey çalışıyor!
kitsu.eb

7
SVG öğelerini DOM yöntemi createElementNS kullanılarak oluşturulduktan sonra jquery ile değiştirebilirsiniz. MakeSVG işlevini 'return $ (el)' olarak değiştirebilirsiniz ve şimdi jquery yöntemlerini kullanabilen bir svg öğeniz var.
Hoffmann

6
Ah! Bu yüzden işe yaramaz. Aynı şeyi biri jQuery'de diğeri D3.js'de olmak üzere iki farklı kütüphane ile denedim . Her ikisi de kullanarak HTML'de tam olarak aynı kaynak çıktı var , ama D3 oluşturulan olanlar yaptı jQuery oluşturulan öğeleri işlemez! D3 kullanmanızı öneririm:d3.select('body').append('svg').attr('width','100%');
chharvey

3
@MadsSkjern: DOM Level 3 LS bunu hiçbir zaman tarayıcılara yapmadı. Orijinal olarak sadece Mozilla DOMParser artık daha geniş bir şekilde destekleniyor ve jQuery destekliyor $.parseXML.
bobince

1
Hala alakalı. bobince HTML5 karmaşa nefret ediyor, ben tüm web karmaşa nefret ediyorum, çünkü hiçbir yerde bu karışıklık nedeniyle kesmek olmadan iyi bir kod bulabilirsiniz . WWW, WWM World Wide Mess olarak yeniden adlandırılmalıdır.
Olivier Pons

149

Kabul edilen cevap çok karmaşık bir yol gösteriyor. Forresto'nun iddia ettiği gibi ettiği gibi , " bunları DOM gezginine ekliyor, ancak ekrana gibi görünüyor " ve bunun nedeni html ve svg için farklı ad alanları.

En kolay çözüm, tüm svg'yi "yenilemek" tir. Daire (veya başka öğeler) ekledikten sonra şunu kullanın:

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

Bu hile yapar. Daire ekranda.

İsterseniz bir container div kullanın:

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

Ve svg konteyner div içine sarın:

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

İşlevsel örnek:
http://jsbin.com/ejifab/1/edit

Bu tekniğin avantajları:

  • mevcut svg'yi (zaten DOM'da olan) düzenleyebilirsiniz, örn. komut dosyası olmadan örnek "sabit kodlanmış" Raphael veya benzeri kullanılarak oluşturuldu.
  • Örneğin, karmaşık eleman yapılarını dize olarak ekleyebilirsiniz. $('svg').prepend('<defs><marker></marker><mask></mask></defs>');jQuery'de yaptığınız gibi.
  • öğeler eklendikten ve ekranda görünür hale getirildikten sonra, $("#cont").html($("#cont").html());öznitelikleri jQuery kullanılarak düzenlenebilir.

DÜZENLE:

Yukarıdaki teknik yalnızca "sabit kodlanmış" veya DOM ile manipüle edilmiş (= document.createElementNS vb.) SVG ile çalışır. Raphael elemanlar oluşturmak için kullanılıyorsa (testlerime göre), Raphael nesneleri ile SVG DOM arasındaki bağlantı $("#cont").html($("#cont").html());kullanılırsa kesilir . Bunun çözümü,$("#cont").html($("#cont").html()); ve bunun yerine sahte SVG belgesi kullanmaktadır.

Bu kukla SVG, ilk olarak SVG belgesinin metinsel bir temsilidir ve yalnızca gerekli öğeleri içerir. İstersek örneğin. Raphael belgesine bir filtre elemanı eklemek için, kukla gibi bir şey olabilir <svg id="dummy" style="display:none"><defs><filter><!-- Filter definitons --></filter></defs></svg>. Metin temsili önce jQuery'nin $ ("body"). Append () yöntemi kullanılarak DOM'a dönüştürülür. Ve (filtre) öğesi DOM'da olduğunda, standart jQuery yöntemleri kullanılarak sorgulanabilir ve Raphael tarafından oluşturulan ana SVG belgesine eklenebilir.

Neden bu kukla gerekli? Neden Raphael tarafından oluşturulan belgeye kesinlikle bir filtre öğesi eklemeyesiniz? Örneğin kullanarak denerseniz. $("svg").append("<circle ... />"), html öğesi olarak oluşturulur ve yanıtlarda açıklandığı gibi hiçbir şey ekranda değildir. Ancak, SVG belgesinin tamamı eklenirse, tarayıcı SVG belgesindeki tüm öğelerin ad alanı dönüştürmesini otomatik olarak işler.

Bir örnek tekniği aydınlatır:

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

Bu tekniğin tam çalışma demosu burada: http://jsbin.com/ilinan/1/edit .

(Henüz bir fikrim yok, $("#cont").html($("#cont").html());Raphael'i kullanırken neden işe yaramıyor. Çok kısa bir hack olur.)


$ (# Picture) .html ile "yeniden düzenleme hilesi" kullanırken SVG'yi işlemek için (Ev yapımı) çözümümü kullanıyorum ve Raphael ile aynı Problemi kullanıyorum, ancak çözümüm önbelleğe alınmış bazı SVG'leri yeniden başlatmaktı elemanları (dikdörtgen, dönüşüm vb.)
18'de yarımbit

Sen bir büyücüsün. Kabul edilen cevaba bakıyordum ve insan gibiydim bu bir acı olacak. Sonra bunu gördüm ve ihtiyacım olan her şeyi kısa bir çizgi içinde yapıyor. Teşekkür ederim!
Besteci

1
BU MERMERLERİMİ KAYBEDMEM İÇİN NEDEN OLDU ... SVG ad alanında sihirli bir şekilde js DOM elms'i düşündüğüm gibi düşündüm ... eklemeleri tanımak için etiket denetçisine sahiptim ... ama şimdiye kadar zar atmadım!
Gus Crawford

Mevcut çokgen ve yol etiketlerimi yeni oluşturulan bir üst svg etiketine eklerken benim için harika çalıştı. Teşekkürler!
Kivak Wolf

1
$("#cont").html($("#cont").html());Chrome'da harika çalıştı, ancak IE 11'de benim için çalışmadı.
CoderDennis

37

Giderek daha popüler olan D3 kütüphanesi svg'yi ekleme / manipüle etme tuhaflıklarını çok iyi idare ediyor. Burada belirtilen jQuery saldırılarının aksine kullanmayı düşünebilirsiniz.

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

güzel cevap. burada bir sorun için bana yardımcı oldu.
ismail baig

jQuery, SVG'den de sınıfları ayarlamayı / almayı seçer. Bir kenara.
QueueHammer

24

JQuery öğelere <svg>eklenemiyor (bunları DOM gezginine ekliyor gibi görünüyor, ancak ekranda değil).

Geçici çözümlerden biri <svg>, sayfaya ihtiyacınız olan tüm öğeleri içeren bir a eklemektir ve ardından öğelerin niteliklerini değiştirmektir .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/


Teşekkürler, bana çok yardımcı oldu! Bu çok iyi bir yaklaşım. <circle>Karmaşık ve yavaş DOM yöntemleri (örn. createDocumentFragment()Veya createElementNS()) kullanarak tek tek eklemek veya başka öğeler eklemek yerine , tüm svg belgesini kaba ekleyin. $ ('Body') yerine elbette bazı $ ('div') da olabilir. Ve bundan sonra svg belgesi DOM'dadır ve örneğin kullanarak tanıdık bir şekilde sorgulanabilir. $ ( 'c'). attr ( 'dolgu', 'kırmızı').
Timo Kähkönen

Buna ek bir örnek daha verdim : jsbin.com/inevaz/1 . İnternetten iframe'e tiger.svg kaynak kodunu alır ve textarea'ya Kopyala Yapıştırılabilir. Daha sonra, textarea içeriği satır içi svg için kaynak olarak kullanılır ve daha sonra DOM aracılığıyla erişilebilir (kontur stilini değiştirmek için).
Timo Kähkönen

Bu harika, neden herkesin işe yaramayan diğer cevaplar için oy verdiğini bilmiyorum.
Dustin Poissant

Bu benim için de en iyi cevaptı. Ben bir svg <kullan xlink: href = "# icon"> gerekli ve bu en kısa çalışan cevap oldu.
Eydamos

17

Birinin bu yöntemden bahsettiğini görmedim ama document.createElementNS() bu durumda yardımcı oldu.

Vanilla Javascript'i doğru ad alanına sahip normal DOM düğümleri olarak ve ardından oradan jQuery-ify kullanarak öğeler oluşturabilirsiniz. Şöyle ki:

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

Tek tarafı, her bir SVG öğesini tek tek doğru ad alanına sahip olmanız gerektiğidir, aksi takdirde çalışmaz.


2
Timo'nun yanıtı (en çok oy alanlar) Chrome'da çalıştı, ancak IE'de çalışmadı. Bu cevap her iki tarayıcı için de sorunu çözdü!
CoderDennis

1
Güzel! Yararlı bilgiler
Chris Dolphin

14

Sahip olduğum tüm tarayıcılarla çalışan kolay bir yol buldum (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>


5
Bu yenileme çizgisi tam da aradığım şeydi. Aptal tarayıcılar. Teşekkürler!
Sam Soffes

8

Firefox'ta 2 şey yaparak daire görebilirim:

1) Dosya html'den xhtml'ye yeniden adlandırılıyor

2) Komut dosyasını olarak değiştirin

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

1
Sekiz yıl sonra ve bu hala yardımcı olur!
Don 01001100

5

@ Chris-dolphin'in cevabına dayanarak ancak yardımcı fonksiyonunu kullanarak:

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

1
Elbette şunları da yapabilirsiniz:$svg.append($s('<circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red"/>'));
Jonas Berlin

4

Eklemeniz gereken dize SVG ise ve uygun ad alanını eklerseniz, dizeyi XML olarak ayrıştırabilir ve üst öğeye ekleyebilirsiniz.

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

3

Bobince'nin kabul ettiği cevap kısa, taşınabilir bir çözümdür. Sadece SVG eklemekle kalmayıp, onu manipüle etmeniz gerekiyorsa, "Pablo" JavaScript kütüphanesini deneyebilirsiniz (yazdım). JQuery kullanıcılarına tanıdık gelecek.

Kod örneğiniz şöyle görünecektir:

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

İşaretleme belirtmeden anında SVG öğeleri de oluşturabilirsiniz:

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

1

Ajax kullanmak ve svg öğesini başka bir sayfadan yüklemek daha iyi olabilir.

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

Href, sayfanın svg ile bulunduğu konumdur. Bu şekilde, html içeriğinin değiştirilmesinden kaynaklanabilecek gerginlik efektlerinden kaçınabilirsiniz. Ayrıca, yüklendikten sonra svg'nin paketini açmayı unutmayın:

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

0

Bugün FF 57 ile benim için çalışıyor:

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

Markalar:

Firefox 57'de görüldüğü gibi bu SVG görüntüsü


Bu, eklediğiniz SVG öğeleri değil, metin içeriği.
Robert Longson

Bu olabilir, ancak statik olarak satır içi SVG'nin ilk sayfa yükünden sonra oluşturulur .
paul_h

Ne yani, sorulan soru değil, metin içeriği değil svg öğeleriyle ilgilidir.
Robert Longson

$(this).clone()bir SVG öğesini klonlamaktadır (ve eğer varsa çocuktur). Kullanım geçmişine bakın text(). İçinde "Sizin Yeni" olan tek bir çay kaşığı alıyorum ve iki çay kaşığı ile bitiyorum, biri "Sizin" ile (siyah) ve diğeri de "Yeni" ile (mavi w / line-through). Jeez, 0 oydan -1 :-(
paul_h düştü

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.

-1

Çok daha basit bir yol, SVG'nizi bir dizeye oluşturmak, bir sarmalayıcı HTML öğesi oluşturmak ve svg dizesini kullanarak HTML öğesine eklemektir $("#wrapperElement").html(svgString). Bu, Chrome ve Firefox'ta iyi çalışıyor.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.