jquery 的 append 不适用于 svg 元素?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3642035/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 15:48:23  来源:igfitidea点击:

jquery's append not working with svg element?

jqueryhtmlsvg

提问by bobince

Assuming this:

假设这个:

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

Why don't I see anything?

为什么我什么都看不到?

回答by bobince

When you pass a markup string into $, it's parsed as HTML using the browser's innerHTMLproperty on a <div>(or other suitable container for special cases like <tr>). innerHTMLcan't parse SVG or other non-HTML content, and even if it could it wouldn't be able to tell that <circle>was supposed to be in the SVG namespace.

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

innerHTMLis not available on SVGElement—it is a property of HTMLElement only. Neither is there currently an innerSVGproperty or other way(*) to parse content into an SVGElement. For this reason you should use DOM-style methods. jQuery doesn't give you easy access to the namespaced methods needed to create SVG elements. Really jQuery isn't designed for use with SVG at all and many operations may fail.

innerHTML在 SVGElement 上不可用——它只是 HTMLElement 的一个属性。目前也没有innerSVG属性或其他方式(*)将内容解析为 SVGElement。出于这个原因,您应该使用 DOM 样式的方法。jQuery 无法让您轻松访问创建 SVG 元素所需的命名空间方法。实际上,jQuery 根本不是为与 SVG 一起使用而设计的,许多操作可能会失败。

HTML5 promises to let you use <svg>without an xmlnsinside a plain HTML (text/html) document in the future. But this is just a parser hack(**), the SVG content will still be SVGElements in the SVG namespace, and not HTMLElements, so you'll not be able to use innerHTMLeven though they looklike part of an HTML document.

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

However, for today's browsers you must use XHTML (properly served as application/xhtml+xml; save with the .xhtml file extension for local testing) to get SVG to work at all. (It kind of makes sense to anyway; SVG is a properly XML-based standard.) This means you'd have to escape the <symbols inside your script block (or enclose in a CDATA section), and include the XHTML xmlnsdeclaration. example:

但是,对于当今的浏览器,您必须使用XHTML(适当地用作application/xhtml+xml; 与 .xhtml 文件扩展名一起保存以进行本地测试)才能让 SVG 工作。(无论如何,SVG 是一种基于 XML 的正确标准。)这意味着您必须转义<脚本块中的符号(或包含在 CDATA 部分中),并包含 XHTMLxmlns声明。例子:

<!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, there's DOM Level 3 LS's parseWithContext, but browser support is very poor. Edit to add: however, whilst you can't inject markup into an SVGElement, you could inject a new SVGElement into an HTMLElement using innerHTML, then transfer it to the desired target. It'll likely be a bit slower though:

*:嗯,有 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>

**: I hate the way the authors of HTML5 seem to be scared of XML and determined to shoehorn XML-based features into the crufty mess that is HTML. XHTML solved these problems years ago.

**:我讨厌 HTML5 的作者似乎害怕 XML 并决心将基于 XML 的特性硬塞进 HTML 的混乱中。XHTML 多年前就解决了这些问题。

回答by Timo K?hk?nen

The accepted answershows too complicated way. As Forresto claims in his answer, "it does seem to add them in the DOM explorer, but not on the screen" and the reason for this is different namespaces for html and svg.

接受的答案也表示复杂的方式。正如 Forresto 在他的回答中声称的那样,“它似乎确实将它们添加到 DOM 资源管理器中,但没有添加到屏幕上”,其原因是 html 和 svg 的命名空间不同。

The easiest workaround is to "refresh" whole svg. After appending circle (or other elements), use this:

最简单的解决方法是“刷新”整个 svg。添加圆(或其他元素)后,使用这个:

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

This does the trick. The circle is on the screen.

这就是诀窍。圆圈出现在屏幕上。

Or if you want, use a container div:

或者,如果需要,请使用容器 div:

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

And wrap your svg inside container div:

并将您的 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>

The functional example:
http://jsbin.com/ejifab/1/edit

功能示例:http:
//jsbin.com/ejifab/1/edit

The advantages of this technique:

这种技术的优点:

  • you can edit existing svg (that is already in DOM), eg. created using Raphael or like in your example "hard coded" without scripting.
  • you can add complex element structures as strings eg. $('svg').prepend('<defs><marker></marker><mask></mask></defs>');like you do in jQuery.
  • after the elements are appended and made visible on the screen using $("#cont").html($("#cont").html());their attributes can be edited using jQuery.
  • 您可以编辑现有的 svg(已经在 DOM 中),例如。使用 Raphael 或类似示例中的“硬编码”创建,无需编写脚本。
  • 您可以将复杂的元素结构添加为字符串,例如。$('svg').prepend('<defs><marker></marker><mask></mask></defs>');就像你在 jQuery 中所做的那样。
  • 在元素被附加并使用$("#cont").html($("#cont").html());它们的属性在屏幕上可见后, 可以使用 jQuery 进行编辑。


EDIT:

编辑:

The above technique works with "hard coded" or DOM manipulated ( = document.createElementNS etc.) SVG only. If Raphael is used for creating elements, (according to my tests) the linking between Raphael objects and SVG DOM is broken if $("#cont").html($("#cont").html());is used. The workaround to this is not to use $("#cont").html($("#cont").html());at all and instead of it use dummy SVG document.

上述技术仅适用于“硬编码”或 DOM 操作(= document.createElementNS 等)SVG。如果使用 Raphael 来创建元素,(根据我的测试)如果$("#cont").html($("#cont").html());使用Raphael 对象和 SVG DOM 之间的链接就会被破坏。对此的解决方法是根本不使用$("#cont").html($("#cont").html());,而是使用虚拟 SVG 文档。

This dummy SVG is first a textual representation of SVG document and contains only elements that are needed. If we want eg. to add a filter element to Raphael document, the dummy could be something like <svg id="dummy" style="display:none"><defs><filter><!-- Filter definitons --></filter></defs></svg>. The textual representation is first converted to DOM using jQuery's $("body").append() method. And when the (filter) element is in DOM, it can be queried using standard jQuery methods and appended to the main SVG document which is created by Raphael.

这个虚拟 SVG 首先是 SVG 文档的文本表示,并且只包含需要的元素。如果我们想要例如。要将过滤器元素添加到 Raphael 文档,虚拟对象可能类似于<svg id="dummy" style="display:none"><defs><filter><!-- Filter definitons --></filter></defs></svg>. 首先使用 jQuery 的 $("body").append() 方法将文本表示转换为 DOM。当(过滤器)元素在 DOM 中时,它可以使用标准的 jQuery 方法进行查询,并附加到由 Raphael 创建的主 SVG 文档中。

Why this dummy is needed? Why not to add a filter element strictly to Raphael created document? If you try it using eg. $("svg").append("<circle ... />"), it is created as html element and nothing is on screen as described in answers. But if the whole SVG document is appended, then the browser handles automatically the namespace conversion of all the elements in SVG document.

为什么需要这个假人?为什么不在 Raphael 创建的文档中严格添加过滤器元素?如果您尝试使用例如。$("svg").append("<circle ... />"),它被创建为 html 元素,并且屏幕上没有任何内容,如答案中所述。但是如果附加了整个 SVG 文档,则浏览器会自动处理 SVG 文档中所有元素的命名空间转换。

An example enlighten the technique:

一个例子启发了技术:

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

Full working demo of this technique is here: http://jsbin.com/ilinan/1/edit.

此技术的完整工作演示在这里:http: //jsbin.com/ilinan/1/edit

( I have (yet) no idea, why $("#cont").html($("#cont").html());doesn't work when using Raphael. It would be very short hack. )

(我(还)不知道,为什么$("#cont").html($("#cont").html());在使用 Raphael 时不起作用。这将是非常短的 hack。)

回答by nategood

The increasingly popular D3library handles the oddities of appending/manipulating svg very nicely. You may want to consider using it as opposed to the jQuery hacks mentioned here.

日益流行的D3库很好地处理了附加/操作 svg 的奇怪问题。您可能需要考虑使用它而不是这里提到的 jQuery hacks。

HTML

HTML

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

Javascript

Javascript

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

回答by forresto

JQuery can't append elements to <svg>(it does seem to add them in the DOM explorer, but not on the screen).

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

One workaround is to append an <svg>with all of the elements that you need to the page, and then modify the attributes of the elements using .attr().

一种解决方法是将<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/

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

回答by Chris Dolphin

I haven't seen someone mention this method but document.createElementNS()is helpful in this instance.

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

You can create the elements using vanilla Javascript as normal DOM nodes with the correct namespace and then jQuery-ify them from there. Like so:

您可以使用 vanilla 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);

The only down side is that you have to create each SVG element with the right namespace individually or it won't work.

唯一的缺点是您必须单独创建具有正确命名空间的每个 SVG 元素,否则它将无法工作。

回答by Matthieu Charbonnier

Found an easy way which works with all browsers I have (Chrome 49, Edge 25, Firefox 44, IE11, Safari 5 [Win], Safari 8 (MacOS)) :

找到了一种适用于我拥有的所有浏览器的简单方法(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>

回答by Topera

I can see circle in firefox, doing 2 things:

我可以在 Firefox 中看到圆圈,做了两件事:

1) Renaming file from html to xhtml

1) 将文件从 html 重命名为 xhtml

2) Change script to

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>

回答by Jonas Berlin

Based on @chris-dolphin 's answer but using helper function:

基于@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);

回答by jdesjean

If the string you need to append is SVG and you add the proper namespace, you can parse the string as XML and append to the parent.

如果您需要附加的字符串是 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))

回答by Premasagar

The accepted answer by Bobince is a short, portable solution. If you need to not only append SVG but also manipulate it, you could try the JavaScript library "Pablo"(I wrote it). It will feel familiar to jQuery users.

Bobince 接受的答案是一个简短的、可移植的解决方案。如果您不仅需要附加 SVG,还需要对其进行操作,您可以尝试使用JavaScript 库“Pablo”(我写的)。jQuery 用户会觉得很熟悉。

Your code example would then look like:

您的代码示例将如下所示:

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

You can also create SVG elements on the fly, without specifying markup:

您还可以动态创建 SVG 元素,而无需指定标记:

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