jqueryの追加はsvg要素で機能しませんか?


199

これを想定すると:

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

何も表示されないのはなぜですか?

jquery  html  svg 

回答:


249

マークアップ文字列をに渡すと$、ブラウザのinnerHTMLプロパティを使用してHTMLとして解析されます<div>(または、次のような特別な場合に適した他のコンテナ)<tr>)。innerHTMLはSVGまたは他の非HTMLコンテンツを解析できません。たとえそれが可能で<circle>あっても、SVG名前空間にあるはずであるとは認識できません。

innerHTMLSVGElementでは使用できません。これはHTMLElementのみのプロパティです。現在もありませんinnerSVGコンテンツをSVGElementに解析するプロパティやその他の方法(*)。このため、DOMスタイルのメソッドを使用する必要があります。jQueryでは、SVG要素の作成に必要な名前空間付きメソッドに簡単にアクセスできません。実際、jQueryはSVGで使用するように設計されておらず、多くの操作が失敗する可能性があります。

HTML5は、プレーンHTML()文書なしで将来使用できる<svg>ことを約束します。ただし、これは単なるパーサーハック(**)であり、SVGコンテンツはSVG名前空間内のSVGElementsであり、HTMLElementsではありません。そのため、見た目は使用できません。xmlnstext/htmlinnerHTML、HTMLドキュメントの一部のよう。

ただし、今日のブラウザーでは、SVGをまったく機能させるために、X HTML(正しく機能するapplication/xhtml+xml;ローカルテスト用に.xhtmlファイル拡張子で保存)を使用する必要があります。(とにかくそれは理にかなっています。SVGは適切にXMLベースの標準です。)つまり<、スクリプトブロック内のシンボルをエスケープし(またはCDATAセクションで囲み)、XHTML xmlns宣言を含める必要があります。例:

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

*:まあ、DOMレベル3 LSのparseWithContextがありますが、ブラウザーのサポートは非​​常に貧弱です。編集して追加:ただし、SVGElementにマークアップを挿入することはできませんが、を使用してHTMLElementに新しいSVGElementを挿入し、innerHTMLそれを目的のターゲットに転送することができます。ただし、少し遅くなる可能性があります。

<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の作成者がXMLを怖がらせているように見えて、XMLベースの機能をHTMLである厄介な混乱に陥らせる決心をしています。XHTMLは数年前にこれらの問題を解決しました。


7
この答えはまだ関係があります!追加した要素がChrome要素インスペクターに表示されるが、レンダリングされないという奇妙なバグがありました。私がhtmlタグでRMB> edit as htmlEnterキーを押すと、すべてが表示されます(ただし、すべてのイベントリスナーは消えます)。この回答を読んだ後、createElement呼び出しをcreateElementNSに変更し、すべてが機能するようになりました!
kitsu.eb 2013

7
DOMメソッドcreateElementNSを使用して作成されたSVG要素は、jqueryで操作できます。makeSVG関数を「return $(el)」に変更すると、jqueryメソッドを使用できるsvg要素が作成されます。
ホフマン

6
ああ!そのため、機能しません。私は、jQueryとD3.jsの 2つの異なるライブラリでまったく同じことを試しました。両方を使用してHTMLでまったく同じソース出力を取得しましたが、D3で生成された要素はレンダリングされましたが、jQueryで生成された要素はレンダリングされませんでした。D3の使用をお勧めします:d3.select('body').append('svg').attr('width','100%');
chharvey

3
@MadsSkjern:DOM Level 3 LSがブラウザーに到達したことはありません。ただし、もともとはMozillaのみのDOMParserがより広くサポートされるようになり、jQueryもサポートしてい$.parseXMLます。
ボビンス2015

1
それはまだ関連しています。bobinceはHTML5の混乱を嫌っています。Web全体の混乱は嫌いです。なぜなら、その混乱のために、ハックなしで適切なコード見つけることはできないからです。WWWはWWM World Wide Messに名前を変更する必要があります。
Olivier Pons 2016

149

受け入れ答えのショーがあまりにも道を複雑。フォレストが彼の答えで主張しているように、「それはそれらをDOMエクスプローラに追加するようですが、画面上ではありません」。この理由は、htmlとsvgの名前空間が異なるためです。

最も簡単な回避策は、SVG全体を「更新」することです。サークル(または他の要素)を追加した後、これを使用します:

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

これでうまくいきます。円は画面上にあります。

または、必要に応じて、コンテナーdivを使用します。

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

そして、svgをコンテナー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>

機能例:
http //jsbin.com/ejifab/1/edit

この手法の利点:

  • たとえば、すでにDOMにある既存のsvgを編集できます。スクリプトを使用せずに「ハードコード」された例でRaphaelなどを使用して作成。
  • 複雑な要素構造を文字列として追加できます。 $('svg').prepend('<defs><marker></marker><mask></mask></defs>');jQueryで行うように。
  • 要素が追加され$("#cont").html($("#cont").html());、属性を使用して画面に表示された後は、 jQueryを使用して編集できます。

編集:

上記の手法は、「ハードコード」またはDOM操作(= document.createElementNSなど)SVGでのみ機能します。要素の作成にRaphaelが使用されている場合、(私のテストによると)RaphaelオブジェクトとSVG DOMの間のリンク$("#cont").html($("#cont").html());は、使用されている場合は壊れています。これの回避策はまったく使用せず$("#cont").html($("#cont").html());、代わりにダミーのSVGドキュメントを使用することです。

このダミーのSVGは、最初はSVGドキュメントのテキスト表現であり、必要な要素のみが含まれています。たとえば、Raphaelドキュメントにフィルター要素を追加する場合、ダミーはのようになります<svg id="dummy" style="display:none"><defs><filter><!-- Filter definitons --></filter></defs></svg>。テキスト表現は、まずjQueryの$( "body")。append()メソッドを使用してDOMに変換されます。(filter)要素がDOMにある場合、標準のjQueryメソッドを使用してクエリを実行し、Raphaelによって作成されたメインのSVGドキュメントに追加できます。

なぜこのダミーが必要なのですか?Raphaelが作成したドキュメントにフィルター要素を厳密に追加しないのはなぜですか?あなたがそれを使ってそれを試すなら $("svg").append("<circle ... />")、html要素として作成され、回答に記載されているように画面には何も表示されません。ただし、SVGドキュメント全体が追加された場合、ブラウザはSVGドキュメント内のすべての要素の名前空間変換を自動的に処理します。

例は技術を啓発します:

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

このテクニックの完全に機能するデモはこちらです:http : //jsbin.com/ilinan/1/edit

(私は(まだ)わから$("#cont").html($("#cont").html());ない、なぜRaphaelを使用するときに動作しないのか。それは非常に短いハックになるだろう。)


$(#picture).htmlで「解析トリック」を使用すると、SVGの処理に(自家製)ソリューションを使用していて、Raphaelと同じ問題があります... しかし、私のソリューションは、キャッシュされたSVGを再初期化することでした要素(四角形のマーキング、変換など)
ハーフビット2014年

あなたは魔法使いです。私は受け入れられた答えを見ていました、そして私はこれが苦痛になるつもりである人のようでした。次に、これを見て、必要なすべてのことを1行で行います。ありがとうございました!
Composer

1
これは私が大理石を失う原因になった...私が思っているように、SVG名前空間でjs DOM elmsを魔法のように使用できると思った...タグインスペクターに挿入を認識させた...しかし、今までさいの目に切っていない!
Gus Crawford

新しく作成した親のsvgタグに既存のポリゴンタグとパスタグを追加するときに、うまくいきました。ありがとう!
Kivak Wolf 2015

1
$("#cont").html($("#cont").html());Chromeではうまくいきましたが、IE 11ではうまくいきませんでした。
CoderDennis 2018年

37

ますます人気のD3ライブラリは、svgの追加/操作の奇妙さを非常にうまく処理しています。ここで述べたjQueryハックとは対照的に、それを使用することを検討する必要があるかもしれません。

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

素敵な答え。ここでいくつかの問題を解決しました。
ismail baig 2014年

jQueryは、SVGからのクラスの設定/取得も制限します。余談です。
QueueHammer

24

JQueryは要素を追加できません<svg>(DOMエクスプローラーでは要素が追加されているようですが、画面では追加されていません)。

1つの回避策は<svg>、ページに必要なすべての要素をに追加し、を使用して要素の属性を変更することです.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/


ありがとう、たくさん助けてくれました!これは非常に良いアプローチです。<circle>複雑で低速のDOMメソッド(例:createDocumentFragment()またはcreateElementNS())を使用して個別に要素を追加する代わりに、svgドキュメント全体をコンテナに追加します。$( 'body')の代わりに、もちろん$( 'div')にすることもできます。そしてその後、svgドキュメントはDOM上にあり、使い慣れた方法でクエリできます。$( 'c')。attr( 'fill'、 'red')。
TimoKähkönen12年

私はこれの追加の例を作りました:jsbin.com/inevaz/1。tiger.svgソースコードをインターネットからiframeに取得し、textareaにコピーして貼り付けることができます。textareaのコンテンツは、インラインsvgのソースとして使用され、DOMを介してアクセスできます(ストロークスタイルを変更するため)。
TimoKähkönen12年

これは素晴らしいことです。なぜ誰もが単に機能しない他の回答に投票している理由はわかりません。
ダスティンポワサント2016年

これも私にとって最良の答えでした。<use xlink:href = "#icon">を使用したsvgが必要でしたが、これが最も短い有効な答えでした。
Eydamos

17

この方法について誰かが言及したことはありませんがdocument.createElementNS()、この場合は役に立ちます。

正しい名前空間を持つ通常のDOMノードとしてバニラJavascriptを使用して要素を作成し、そこからjQuery化することができます。そのようです:

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

唯一の欠点は、適切な名前空間を使用して各SVG要素を個別に作成する必要があることです。そうしないと機能しません。


2
Timoの回答(上位投票)はChromeでは機能しましたが、IEでは機能しませんでした。この回答で両方のブラウザの問題が解決しました!
CoderDennis 2018年

1
いいね!知っておきたいこと
クリスドルフィン

14

私が持っているすべてのブラウザー(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
そのリフレッシュラインはまさに私が探していたものです。ばかげたブラウザ。ありがとう!
Sam Soffes 2017年

8

私は2つのことをしてFirefoxでサークルを見ることができます:

1)htmlからxhtmlへのファイル名の変更

2)スクリプトを次のように変更します

<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
8年後、これはまだ役に立ちます!
Don 01001100

5

@ chris-dolphinの回答に基づいていますが、ヘルパー関数を使用しています:

// 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
あなたはもちろんも行うことができます$svg.append($s('<circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red"/>'));
ジョナスベルリン

4

追加する必要がある文字列がSVGであり、適切な名前空間を追加する場合、文字列をXMLとして解析し、親に追加できます。

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が認めた回答は、短くてポータブルなソリューションです。SVGを追加するだけでなく、操作する必要がある場合は、JavaScriptライブラリー「Pablo」を試すことができます(私が作成しました)。jQueryユーザーにとってはなじみ深いものです。

コード例は次のようになります。

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

マークアップを指定せずに、オンザフライでSVG要素を作成することもできます。

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

1

私はajaxを使用して別のページからsvg要素をロードする方が良いかもしれないと提案します。

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

ここで、hrefはsvgのあるページの場所です。このようにして、htmlコンテンツの置き換えによって発生する可能性のあるジッター効果を回避できます。また、svgがロードされた後、そのラップを解除することを忘れないでください:

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

0

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

作る:

Firefox 57で見られるこのSVG画像


これは、追加するSVG要素ではなく、テキストコンテンツです。
ロバートロングソン、2018年

可能性はありますが、静的にインライン化されたSVGの最初のページのロードにレンダリングされます。
paul_h

つまり、質問されているのではなく、テキストコンテンツではなくsvg要素についてです。
ロバートロングソン2018年

$(this).clone()SVG要素のクローンを作成しています(そして、もしあればそれの子です)。の使用を過ぎて見てくださいtext()。私は「Your New」が含まれている1つのtspanを取得し、2つのtspanで終了しています。Jeez、0票から-1に
減少

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

より簡単な方法は、SVGを文字列に生成し、ラッパーHTML要素を作成し、を使用してHTML要素にsvg文字列を挿入すること $("#wrapperElement").html(svgString)です。これはChromeとFirefoxでは問題なく機能します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.