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>
为什么我什么都看不到
将标记string传递给$
,将使用浏览器的<div>
(或其他适合的容器,如<tr>
)的innerHTML
属性将其parsing为HTML。 innerHTML
无法parsingSVG或其他非HTML内容,即使它可能不能分辨<circle>
应该在SVG名称空间中。
innerHTML
在SVGElement上不可用 – 它仅是HTMLElement的一个属性。 目前还没有innerSVG
属性或其他方式(*)来parsingSVGElement的内容。 出于这个原因,你应该使用DOM风格的方法。 jQuery不会让您轻松访问创buildSVG元素所需的名称空间方法。 真的,jQuery不是专门用于SVG的,所以很多操作可能会失败。
HTML5承诺可以让您在未来使用<svg>
而无需在纯HTML( text/html
)文档中使用xmlns
。 但这只是一个parsing器hack(**),SVG内容仍然是SVG命名空间中的SVGElements,而不是HTMLElements,所以即使它们看起来像HTML文档的一部分,也不能使用innerHTML
。
但是,对于今天的浏览器,你必须使用XHTML(正确地作为application/xhtml+xml
;保存为本地testing的.xhtml文件扩展名)才能使SVG正常工作。 (无论如何,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多年前解决了这些问题。
接受的答案显示得太复杂了。 正如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>
function的例子:
http://jsbin.com/ejifab/1/edit
这种技术的优点:
- 你可以编辑现有的svg(已经在DOM中),例如。 在你的例子中使用拉斐尔或类似的“硬编码”创build,而无需编写脚本。
- 你可以添加复杂的元素结构作为string例如。
$('svg').prepend('<defs><marker></marker><mask></mask></defs>');
就像你在jQuery中做的一样。 - 在元素被附加后,使用
$("#cont").html($("#cont").html());
他们的属性可以使用jQuery进行编辑。
编辑:
上述技术仅适用于“硬编码”或DOM操作(= document.createElementNS等)的SVG。 如果使用Raphael创build元素(根据我的testing),如果$("#cont").html($("#cont").html());
则断开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创build的主SVG文档中。
为什么这个假人是需要的? 为什么不给Raphael添加一个过滤元素? 如果你尝试使用例如。 $("svg").append("<circle ... />")
,它被创build为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时不起作用,这将是非常短的。
日益stream行的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");
JQuery不能将元素附加到<svg>
(它似乎将它们添加到DOM资源pipe理器中,而不是在屏幕上)。
一种解决方法是将所需的所有元素附加到<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 }); });
我可以看到在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>
我还没有看到有人提到这个方法,但document.createElementNS()
在这个例子中是有帮助的。
你可以使用vanilla Javascript作为正常的DOM节点创build元素,然后使用正确的名称空间,然后使用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);
唯一的缺点是你必须单独创build每个具有正确命名空间的SVG元素,否则它将无法工作。
根据@ 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);
如果需要添加的string是SVG,并且添加了适当的名称空间,则可以将stringparsing为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))
Bobince公认的答案是一个简短的便携式解决scheme。 如果你不仅需要附加SVG,而且还要操纵它,你可以尝试JavaScript库“Pablo” (我写的)。 jQuery用户会感觉很熟悉。
您的代码示例将如下所示:
$(document).ready(function(){ Pablo("svg").append('<circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red"/>'); });
您也可以随时创buildSVG元素,而无需指定标记:
var circle = Pablo.circle({ cx:100, cy:50, r:40 }).appendTo('svg');
find一个简单的方法,适用于我所有的浏览器(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"/>'); // 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>
更简单的方法是将SVG生成一个string,创build一个包装HTML元素,并使用$(“#wrapperElement”).html(svgString)将svgstring插入到HTML元素中。 这在Chrome和Firefox中运行得很好。