jquery 的追加不适用于 svg 元素?
假设:
<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>
为什么我看不到任何东西?
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(18)
这对我今天使用 FF 57 有用:
使:
This is working for me today with FF 57:
Makes:
无论如何它都不会工作,除非你将它编码为 Base64。即使使用 jQuery 3,即使使用 hacks,您最终也会将 SVG 编码为 Base64(并将它们放入 ),因为否则复杂的图形(例如使用 Illustrator 生成的 SVG)将无法渲染 - 将会丢失对象等因此,如果您希望将外部 SVG 图像动态添加到现有的 Web 树中,则应始终附加外部 SVG 图像或将其编码为 Base64。
It won't work anyway, unless you encode it as Base64. Even with jQuery 3, even with hacks, you'll end up with encoding SVGs as Base64 (and putting them into ), because otherwise complex graphics, such as SVGs generated with Illustrator, will fail to render — there'll be missing objects etc. So you shall always either attach external SVG images or encode them as Base64 if you desire to add them lively to existing web tree.
替代方案 1:原生 js
insertAdjacentHTML()
如果您根本不考虑切换到原生 JavaScript ...
您还可以使用本机 javaScript 方法
insertAdjacentHTML()
来获得类似的方便表示法。$("#svg")[0]
使您的 在本机 JS 中可选择 jQuery 对象。选项
第一个参数定义新元素的附加位置。
:before
):after
)替代方案 2:编写原生 js
DOMParser()
帮助器mdn 网络文档:DOMParser.parseFromString()
jQuery 中的用法:
演示
jquery 的
after()
或before()
方法也将无法添加 SVG DOM(取决于正确的命名空间)元素。使用上述解决方法也可以解决此问题。
“更新”2024 JQuery 4.0.0 beta
最新jQuery 版本 4 显然没有解决所描述的问题。
Alternative 1: native js
insertAdjacentHTML()
Provided you don't consider switching to native JavaScript at all ...
You could also use native javaScript method
insertAdjacentHTML()
for similarly convenient notation.$("#svg")[0]
makes your jQuery object selectable in native JS.Options
The first parameter defines where the new element(s) are appended.
:before
):after
)Alternative 2: write a native js
DOMParser()
helpermdn web docs: DOMParser.parseFromString()
Usage in jQuery:
Demo
jquery's
after()
orbefore()
methods will also fail to add a SVG DOM (depending on the correct namespace) element.Using the aforementioned workarounds will fix this issue as well.
"Update" 2024 JQuery 4.0.0 beta
The latest version 4 of jQuery apparently haven't fixed the described issue.
使用 jquery 你就可以做到这一点。
将数据类型设置为“文本”。
With jquery you can do just that.
setting the DataType to 'text'.
我为此做了一个小功能。至于 jQuery 追加方法,问题是需要为 SVG 指定命名空间,即
"http://www.w3.org/2000/svg"
更多那么,如果我为追加方法准备它会怎么样?在这种情况下,您唯一需要提供的是一些参数,例如:
tagName
:它可以是每个 SVG 元素,如 rect、circle、text、g 等。text
:如果如果您正在使用文本标记名之类的内容,则需要为 SVG 元素指定文本和其他已知属性。
因此,我要做的是定义一个名为
createSvgElem()
的函数,该函数在内部使用 document.createElementNS() 。这是一个例子:
这是函数:
这里你可以自己尝试一下:
I have made a small function for that. As for jQuery append method, the problem is the requirement for specifying namespace for SVG which is
"http://www.w3.org/2000/svg"
MoreSo what if I prepare it for append method? In that case the only thing you need to offer is some parameters like:
tagName
: It can be every SVG element like rect, circle, text, g etc.text
: If you are using something like text tagname, you'll need to specify textAnd other known attributes for an SVG elements.
Thus what I'm going to do is defining a function named
createSvgElem()
which uses document.createElementNS() internally.Here is an example:
And here's the function:
Here you can try it yourself:
一种更简单的方法是将 SVG 生成为字符串,创建一个包装 HTML 元素,然后使用
$("#wrapperElement").html(svgString)
将 svg 字符串插入到 HTML 元素中。这在 Chrome 和 Firefox 中运行得很好。A much simpler way is to just generate your SVG into a string, create a wrapper HTML element and insert the svg string into the HTML element using
$("#wrapperElement").html(svgString)
. This works just fine in Chrome and Firefox.我建议最好使用 ajax 并从另一个页面加载 svg 元素。
其中 href 是带有 svg 的页面的位置。这样您就可以避免替换 html 内容时可能出现的任何抖动效果。另外,不要忘记在加载 svg 后将其解开:
I would suggest it might be better to use ajax and load the svg element from another page.
Where href is the location of the page with the svg. This way you can avoid any jittery effects that might occur from replacing the html content. Also, don't forget to unwrap the svg after it's loaded:
Bobince 接受的答案是一个简短、便携的解决方案。如果您不仅需要附加 SVG 还需要对其进行操作,您可以尝试 JavaScript 库“Pablo” (我写的) 。 jQuery 用户会感觉很熟悉。
您的代码示例将如下所示:
您还可以动态创建 SVG 元素,而不指定标记:
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.
Your code example would then look like:
You can also create SVG elements on the fly, without specifying markup:
如果您需要附加的字符串是 SVG 并且添加了正确的命名空间,则可以将该字符串解析为 XML 并附加到父级。
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.
我可以在 Firefox 中看到圆圈,做两件事:
1)将文件从 html 重命名为 xhtml
2)将脚本更改为
I can see circle in firefox, doing 2 things:
1) Renaming file from html to xhtml
2) Change script to
基于 @chris-dolphin 的答案,但使用辅助函数:
Based on @chris-dolphin 's answer but using helper function:
找到了一种适用于我拥有的所有浏览器的简单方法(Chrome 49、Edge 25、Firefox 44、IE11、Safari 5 [Win]、Safari 8 (MacOS)):
Found an easy way which works with all browsers I have (Chrome 49, Edge 25, Firefox 44, IE11, Safari 5 [Win], Safari 8 (MacOS)) :
我还没有看到有人提到这个方法,但是
document.createElementNS()
在这种情况下很有帮助。您可以使用普通 Javascript 创建元素作为具有正确命名空间的普通 DOM 节点,然后从那里对它们进行 jQuery 化。像这样:
唯一的缺点是您必须使用正确的命名空间单独创建每个 SVG 元素,否则它将无法工作。
I haven't seen someone mention this method but
document.createElementNS()
is helpful in this instance.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:
The only down side is that you have to create each SVG element with the right namespace individually or it won't work.
日益流行的 D3 库很好地处理了附加/操作 svg 的奇怪问题。您可能需要考虑使用它,而不是这里提到的 jQuery hacks。
HTML
JavaScript
The increasingly popular D3 library handles the oddities of appending/manipulating svg very nicely. You may want to consider using it as opposed to the jQuery hacks mentioned here.
HTML
Javascript
JQuery 无法将元素附加到
(它似乎确实将它们添加到 DOM 资源管理器中,但不在屏幕上)。
一种解决方法是将
附加到页面所需的所有元素,然后使用
.attr()
修改元素的属性。http://jsfiddle.net/8FBjb/1/
JQuery can't append elements to
<svg>
(it does seem to add them in the DOM explorer, but not on the screen).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()
.http://jsfiddle.net/8FBjb/1/
接受的答案显示的方式过于复杂。正如 Forresto 在他的回答中声称的那样,“它似乎确实将它们添加到 DOM 资源管理器中,但没有添加到screen”,其原因是 html 和 svg 的命名空间不同。
最简单的解决方法是“刷新”整个 svg。添加圆(或其他元素)后,使用这个:
这可以解决问题。圆圈出现在屏幕上。
或者,如果您愿意,可以使用容器 div:
并将 svg 包装在容器 div 内:
功能示例:
http://jsbin.com/ejifab/1/edit
这种技术的优点:
$('svg').prepend(' ');
像你一样在 jQuery 中。$("#cont").html($("#cont").html()); 附加元素并在屏幕上可见后,可以使用 jQuery 编辑它们的属性。
编辑:
上述技术仅适用于“硬编码”或 DOM 操作(= document.createElementNS 等)SVG。如果 Raphael 用于创建元素,(根据我的测试)如果
$("#cont").html($("#cont").html()) Raphael 对象和 SVG DOM 之间的链接就会被破坏使用;
。解决方法是根本不使用$("#cont").html($("#cont").html());
,而是使用虚拟 SVG 文档。这个虚拟 SVG 首先是 SVG 文档的文本表示,并且仅包含所需的元素。如果我们想要例如。要将过滤器元素添加到 Raphael 文档,虚拟元素可能类似于
。首先使用 jQuery 的 $("body").append() 方法将文本表示转换为 DOM。当 (filter) 元素位于 DOM 中时,可以使用标准 jQuery 方法对其进行查询,并将其附加到 Raphael 创建的主 SVG 文档中。
为什么需要这个假人?为什么不严格向 Raphael 创建的文档添加过滤元素?如果你尝试使用例如。
$("svg").append("")
,它被创建为 html 元素,并且屏幕上没有任何内容,如答案中所述。 但是,如果附加整个 SVG 文档,则浏览器会自动处理 SVG 文档中所有元素的命名空间转换。示例说明该技术:
该技术的完整工作演示如下:http://jsbin.com/ilinan/1/edit。
(我(还)不知道为什么
$("#cont").html($("#cont").html());
在使用 Raphael 时不起作用。它会非常简短。)The accepted answer shows 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.
The easiest workaround is to "refresh" whole svg. After appending circle (or other elements), use this:
This does the trick. The circle is on the screen.
Or if you want, use a container div:
And wrap your svg inside container div:
The functional example:
http://jsbin.com/ejifab/1/edit
The advantages of this technique:
$('svg').prepend('<defs><marker></marker><mask></mask></defs>');
like you do in jQuery.$("#cont").html($("#cont").html());
their attributes can be edited using 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.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.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.An example enlighten the technique:
Full working demo of this technique is here: 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. )当您将标记字符串传递到
$
时,它会使用(或其他合适的容器)上浏览器的
innerHTML
属性将其解析为 HTML对于像这样的特殊情况)。
innerHTML
无法解析 SVG 或其他非 HTML 内容,即使可以,它也无法判断
应该位于SVG 命名空间。innerHTML
在 SVGElement 上不可用——它只是 HTMLElement 的一个属性。目前也没有innerSVG
属性或其他方式 (*) 将内容解析为 SVGElement。因此,您应该使用 DOM 风格的方法。 jQuery 无法让您轻松访问创建 SVG 元素所需的命名空间方法。实际上,jQuery 根本不是为与 SVG 一起使用而设计的,许多操作可能会失败。HTML5 承诺让您将来可以在纯 HTML (
text/html
) 文档中使用,而无需使用
xmlns
。但这只是一个解析器 hack(**),SVG 内容仍然是 SVG 命名空间中的 SVGElements,而不是 HTMLElements,因此您将无法使用innerHTML
,即使它们 看起来就像 HTML 文档的一部分。但是,对于当今的浏览器,您必须使用 XHTML(正确地充当
application/xhtml+xml
;使用 .xhtml 文件扩展名保存以进行本地测试)才能使 SVG 正常工作根本不。 (无论如何,这有点有意义;SVG 是一个完全基于 XML 的标准。)这意味着您必须转义脚本块内的<
符号(或包含在 CDATA 部分中) ,并包含 XHTMLxmlns
声明。例如:*:嗯,有 DOM Level 3 LS 的 parseWithContext,但是浏览器支持很差。编辑添加:但是,虽然您无法将标记注入到 SVGElement 中,但您可以使用
innerHTML
将新的 SVGElement 注入到 HTMLElement 中,然后将其传输到所需的目标。不过,它可能会慢一些:**:我讨厌 HTML5 的作者似乎害怕 XML,并决定将基于 XML 的功能硬塞到 HTML 的混乱中。 XHTML 多年前就解决了这些问题。
When you pass a markup string into
$
, it's parsed as HTML using the browser'sinnerHTML
property on a<div>
(or other suitable container for special cases like<tr>
).innerHTML
can'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
is not available on SVGElement—it is a property of HTMLElement only. Neither is there currently aninnerSVG
property 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.HTML5 promises to let you use
<svg>
without anxmlns
inside 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 useinnerHTML
even though they look like part of an HTML document.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 XHTMLxmlns
declaration. example:*: 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:**: 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.