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 

Answers:


249

当您将标记字符串传递到时$,会使用(或其他适用于特殊情况的容器)容器innerHTML上的浏览器属性将其解析为HTML 。无法解析SVG或其他非HTML内容,即使可以,也无法分辨出该内容应该在SVG命名空间中。<div><tr>innerHTML<circle>

innerHTML在SVGElement上不可用-它仅是HTMLElement的属性。当前没有innerSVG属性或其他方法(*)将内容解析为SVGElement。因此,您应该使用DOM样式的方法。jQuery不允许您轻松访问创建SVG元素所需的命名空间方法。确实,jQuery并不是为与SVG一起使用而设计的,许多操作可能会失败。

HTML5承诺将来会在<svg>没有xmlns纯HTML(text/html)文档的情况下使用。但这只是一个解析器hack(**),SVG内容仍将是SVG命名空间中的SVGElements,而不是HTMLElements,因此innerHTML即使它们看起来像HTML文档的一部分,也将无法使用。

但是,对于当今的浏览器,您必须使用X HTML(正确用作application/xhtml+xml;以.xhtml文件扩展名保存以进行本地测试)才能使SVG完全正常工作。(无论如何,这都是有意义的; 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 Level 3 LS的parseWithContext,但是浏览器支持非常差。编辑添加:但是,虽然不能将标记注入SVGElement中,但是可以使用来将新的SVGElement注入HTMLElement中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元素检查器中,但无法渲染。如果I RMB> edit as html在html标记上并按Enter,则显示所有内容(但所有事件侦听器均消失)。阅读此答案后,我将createElement调用更改为createElementNS,现在一切正常!
kitsu.eb

7
一旦使用DOM方法createElementNS创建了SVG元素,就可以使用jquery对其进行操作。您可以将makeSVG函数更改为'return $(el)',现在有了可以使用jquery方法的svg元素。
霍夫曼

6
啊! 这就是为什么它不起作用的原因。我用两种不同的库尝试了完全相同的东西,一种在jQuery中,另一种在D3.js中。使用这两者,我在HTML中获得了完全相同的源输出,但是jQuery生成的元素无法渲染,而D3生成的元素却可以渲染!我建议使用D3:d3.select('body').append('svg').attr('width','100%');
chharvey 2014年

3
@MadsSkjern:DOM Level 3 LS从未进入浏览器。不过,现在更广泛地支持最初仅用于Mozilla的DOMParser,而jQuery具有$.parseXML
bobince 2015年

1
仍然有意义。bobince讨厌HTML5的混乱局面,我讨厌整个网络的混乱局面,因为在这种情况下,没有地方没有黑客就可以找到好的代码。WWW应该重命名为WWM World Wide Mess。
奥利维尔·庞斯

149

接受的答案也表示复杂的方式。正如Forresto在他的回答中声称的那样,“ 它似乎确实是在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

该技术的优点:

  • 您可以编辑现有的svg(已经在DOM中),例如。使用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时不起作用。那将是很短的hack。)


我将我的(自制)解决方案用于处理SVG时,与$(#picture).html一起使用“解析技巧”时,与Raphael有相同的问题, 但是我的解决方案是重新初始化一些缓存的SVG元素(标记矩形,变换等)
Halfbit

您是向导。我一直在寻找被接受的答案,就像男人一样,这会很痛苦。然后,我看到了这一点,并且它在短短一行内就完成了我需要的一切。谢谢!
作曲家

1
这是让我失去我的麻烦的原因。。。我以为我可以像人们可能认为的那样在SVG名称空间上神奇地使用js DOM榆木...让标签检查器可以识别插入内容...但是到目前为止还没有骰子!
Gus Crawford

将现有的多边形和路径标签附加到新创建的父svg标签时,对我来说非常有用。谢谢!
基瓦克·沃尔夫

1
$("#cont").html($("#cont").html());在Chrome中效果很好,但对我来说在IE 11中却不起作用。
CoderDennis

37

越来越流行的D3库很好地处理了添加/操作svg的怪癖。您可能要考虑使用它,而不是这里提到的jQuery技巧。

的HTML

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

Java脚本

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

好答案。它帮助我解决了一些问题。
ismail baig 2014年

jQuery也对从SVG设置/获取类感到cho恼。放在一边。
QueueHammer 2014年

24

jQuery无法将元素追加到<svg>(它似乎确实将它们添加到DOM资源管理器中,但不在屏幕上)。

一种解决方法是将所需的<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önen2012年

我为此做了一个额外的示例:jsbin.com/inevaz/1。它从互联网上获取Tiger.svg源代码到iframe,并且可以将其复制粘贴到textarea。然后将textarea的内容用作内嵌svg的源,然后可以通过DOM访问内嵌svg(以更改笔触样式)。
TimoKähkönen2012年

太好了,我不知道为什么每个人都为那些根本行不通的其他答案投票。
达斯汀·普桑桑

这对我也是最好的答案。我需要一个<use xlink:href =“#icon”>的svg,这是最短的工作答案。
Eydamos

17

我还没有看到有人提到这种方法,但是document.createElementNS()在这种情况下很有帮助。

您可以使用香草Javascript作为具有正确名称空间的普通DOM节点创建元素,然后从此处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

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
那条刷新线正是我想要的。愚蠢的浏览器。谢谢!
山姆·索菲斯

8

我可以在firefox中看到圆圈,做了2件事:

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
八年后,这仍然很有帮助!
唐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元素,而是文本内容。
罗伯特·朗森

可能是这样,但是静态内联SVG的初始页面加载呈现。
paul_h

那么,不是要问的问题,这是关于svg元素而不是文本内容。
罗伯特·朗森

$(this).clone()正在克隆SVG元素(如果有,则为子元素)。回顾过去的用法text()。我正在一个单一的tspan中,上面有“您的新货”,最后是两个tspan,一个中有“您的”(黑色),一个有“新”(蓝色,带直通)。Jeez,从0票降至-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.

-1

一种更简单的方法是将SVG生成为字符串,创建包装HTML元素,然后使用以下命令将svg字符串插入HTML元素 $("#wrapperElement").html(svgString)。在Chrome和Firefox中可以正常工作。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.