jquery append 不适用于 svg 元素?
Posted
技术标签:
【中文标题】jquery append 不适用于 svg 元素?【英文标题】:jquery's append not working with svg element? 【发布时间】:2011-04-08 04:59:46 【问题描述】:假设:
<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- 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" >
</svg>
</body>
为什么我什么都看不到?
【问题讨论】:
【参考方案1】:当您将标记字符串传递到$
时,它会使用浏览器在<div>
上的innerHTML
属性解析为HTML(或其他适合特殊情况的容器,例如<tr>
)。 innerHTML
无法解析 SVG 或其他非 HTML 内容,即使可以解析,它也无法判断 <circle>
应该在 SVG 命名空间中。
innerHTML
在 SVGElement 上不可用——它只是 HTMLElement 的属性。目前也没有 innerSVG
属性或其他方式 (*) 将内容解析为 SVGElement。出于这个原因,您应该使用 DOM 样式的方法。 jQuery 无法让您轻松访问创建 SVG 元素所需的命名空间方法。真的,jQuery 根本不是为与 SVG 一起使用而设计的,许多操作可能会失败。
HTML5 承诺将来让您在纯 HTML (text/html
) 文档中使用不带 xmlns
的 <svg>
。但这只是一个解析器 hack(**),SVG 内容仍将是 SVG 命名空间中的 SVGElements,而不是 HTMLElements,因此您将无法使用 innerHTML
,即使它们看起来 就像 HTML 文档的一部分。
但是,对于当今的浏览器,您必须使用 XHTML(正确地用作 application/xhtml+xml
;保存为 .xhtml 文件扩展名以进行本地测试)才能使 SVG 正常工作。 (无论如何,这有点道理;SVG 是一个适当的基于 XML 的标准。)这意味着您必须在脚本块中转义 <
符号(或包含在 CDATA 部分中),并包含 XHTML @ 987654338@声明。示例:
<!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,但您可以使用 innerHTML
将新的 SVGElement 注入 HTMLElement,然后将其传输到所需的目标。不过可能会慢一些:
<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- fill="red" onmousedown="alert(\'hello\');"/>'
));
]]></script>
**:我讨厌 HTML5 的作者似乎害怕 XML,并决心将基于 XML 的功能硬塞进 HTML 的混乱中。 XHTML 多年前就解决了这些问题。
【讨论】:
这个答案仍然是相关的!我刚刚遇到了一个奇怪的错误,其中添加的元素显示在 Chrome 元素检查器中,但不会呈现。如果我在 html 标记上RMB
>edit as html
并点击输入,所有内容都会显示(但所有事件侦听器都消失了)。阅读此答案后,我将 createElement 调用更改为 createElementNS,现在一切正常!
使用 DOM 方法 createElementNS 创建 SVG 元素后,您可以使用 jquery 对其进行操作。您可以将 makeSVG 函数更改为“return $(el)”,现在您有了一个可以使用 jquery 方法的 svg 元素。
啊!所以这就是为什么它不起作用。我用两个不同的库尝试了完全相同的东西,一个在 jQuery 中,一个在 D3.js 中。我使用两者在 HTML 中得到了 exactly 相同的源输出,但是 jQuery 生成的元素不会呈现,而 D3 生成的元素会呈现!我推荐使用 D3:d3.select('body').append('svg').attr('width','100%');
@MadsSkjern:DOM Level 3 LS 从未进入浏览器。不过,原本只有 Mozilla 的 DOMParser 现在得到了更广泛的支持,而 jQuery 拥有 $.parseXML
。
它仍然相关。 bobince 讨厌 HTML5 的混乱,我讨厌整个网络的混乱,因为由于混乱,您在没有 hacks 的情况下找不到好的代码。 WWW应该更名为WWM World Wide Mess。【参考方案2】:
accepted answer 显示方式过于复杂。正如 Forresto 在 his answer 中声称的那样,“它似乎确实将它们添加到 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" >
</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 用于创建元素,(根据我的测试)如果使用 $("#cont").html($("#cont").html());
,Raphael 对象和 SVG DOM 之间的链接将被破坏。解决方法是根本不使用$("#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 元素(标记矩形、转换等) 你是个巫师。我正在查看已接受的答案,我就像男人一样,这会很痛苦。然后,我看到了这个,它在一条短线中完成了我需要的一切。谢谢! 这让我失去了我的大理石......我想我可以像人们想象的那样在 SVG 命名空间上神奇地使用 js DOM 榆树......让标签检查器识别插入......但是直到现在才掷骰子! 在将我现有的多边形和路径标签附加到新创建的父 svg 标签时非常适合我。谢谢!$("#cont").html($("#cont").html());
在 Chrome 中运行良好,但对我来说在 IE 11 中无法运行。【参考方案3】:
越来越流行的D3 库很好地处理了附加/操作 svg 的奇怪之处。您可能需要考虑使用它,而不是这里提到的 jQuery hacks。
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 设置/获取类。顺便说一句。【参考方案4】: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/
【讨论】:
谢谢,帮了我很多忙!这是一个非常好的方法。不要使用复杂而缓慢的 DOM 方法(例如createDocumentFragment()
或 createElementNS()
)单独附加 <circle>
或其他元素,而是将整个 svg 文档附加到容器中。而不是 $('body') 它当然也可以是一些 $('div') 。之后,svg 文档位于 DOM 上,可以使用例如熟悉的方式进行查询。 $('c').attr('fill','red').
我又做了一个例子:jsbin.com/inevaz/1。它从 Internet 获取tiger.svg 源代码到 iframe,并且可以复制粘贴到 textarea。然后将 textarea 的内容用作内联 svg 的源,然后可以通过 DOM 访问(以更改笔触样式)。
这太好了,我不知道为什么每个人都在投票支持那些根本不起作用的其他答案。
这对我来说也是最好的答案。我需要一个带有 【参考方案5】:
我还没有看到有人提到这种方法,但document.createElementNS()
在这种情况下很有帮助。
您可以使用 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);
唯一的缺点是您必须单独使用正确的命名空间创建每个 SVG 元素,否则它将无法工作。
【讨论】:
Timo 的回答(票数最高)在 Chrome 中有效,但在 IE 中无效。这个答案解决了两个浏览器的问题! 不错!很高兴知道【参考方案6】:找到了一种适用于我所有浏览器(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" ></svg>
</div>
<span>It works if two shapes (one square and one circle) are displayed above.</span>
【讨论】:
那条刷新线正是我想要的。愚蠢的浏览器。谢谢!【参考方案7】:我可以在 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>
【讨论】:
八年后,这仍然有用!【参考方案8】:基于@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);
【讨论】:
当然也可以:$svg.append($s('<circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red"/>'));
【参考方案9】:
Bobince 接受的答案是一个简短的便携式解决方案。如果您不仅需要附加 SVG,还需要对其进行操作,您可以尝试 JavaScript library "Pablo"(我写的)。 jQuery 用户会觉得它很熟悉。
您的代码示例将如下所示:
$(document).ready(function()
Pablo("svg").append('<circle cx="100" cy="50" r="40" stroke="black" stroke- fill="red"/>');
);
您也可以即时创建 SVG 元素,无需指定标记:
var circle = Pablo.circle(
cx:100,
cy:50,
r:40
).appendTo('svg');
【讨论】:
【参考方案10】:如果您需要附加的字符串是 SVG 并且您添加了正确的命名空间,您可以将字符串解析为 XML 并附加到父级。
var xml = jQuery.parseXML('<circle xmlns="http://www.w3.org/2000/svg" cx="100" cy="50" r="40" stroke="black" stroke- fill="red"/>');
$("svg").append(xml.documentElement))
【讨论】:
【参考方案11】:我建议最好使用 ajax 并从另一个页面加载 svg 元素。
$('.container').load(href + ' .svg_element');
href 是带有 svg 的页面的位置。这样,您可以避免因替换 html 内容而可能出现的任何抖动效果。另外,不要忘记在 svg 加载后打开它:
$('.svg_element').unwrap();
【讨论】:
【参考方案12】:这对我来说今天适用于 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");
制作:
【讨论】:
您添加的不是 SVG 元素,而是文本内容。 可能是这样,但它在静态内联 SVG 的初始页面加载之后呈现。 那又如何,不是要问的问题,而是关于 svg 元素而不是文本内容的问题。$(this).clone()
正在克隆一个 SVG 元素(如果有的话,它就是子元素)。过去使用text()
。我正在使用一个包含“Your New”的 tspan,最后得到两个 tspan,一个带有“Your”(黑色),一个带有“New in it”(蓝色 w/line-through)。天哪,从 0 票降至 -1 :-(【参考方案13】:
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.
【讨论】:
【参考方案14】:我为此做了一个小功能。至于 jQuery append 方法,问题是需要为 SVG 指定命名空间 "http://www.w3.org/2000/svg"
More
那么,如果我为 append 方法准备它呢?在这种情况下,您唯一需要提供的就是一些参数,例如:
tagName
: 可以是每个 SVG 元素,比如 rect、circle、text、g 等。
text
: 如果你使用的是文本标记名,你需要指定文本
以及 SVG 元素的其他已知属性。
因此,我要做的是定义一个名为 createSvgElem()
的函数,它在内部使用 document.createElementNS()。
这是一个例子:
$("svg").append(
createSvgElem(tagName: "text", x: 10, y: 10, text: "ABC", style: "fill: red")
)
函数如下:
function createSvgElem(options)
var settings = $.extend(
, options);
if(!$.isEmptyObject(settings.tagName))
var el = document.createElementNS('http://www.w3.org/2000/svg', settings.tagName);
for (var k in settings)
if(k != "tagName" && k != "text" && settings[k] != "")//If attribute has value
el.setAttribute(k, settings[k]);
if ("text" in settings)
el.textContent = settings.text; //el.innerText; For IE
return el;
这里你可以自己试试:
//Definition:
function createSvgElem(options)
var settings = $.extend(
, options);
if(!$.isEmptyObject(settings.tagName))
var el = document.createElementNS('http://www.w3.org/2000/svg', settings.tagName);
for (var k in settings)
if(k != "tagName" && k != "text" && settings[k] != "")//If attribute has value
el.setAttribute(k, settings[k]);
if ("text" in settings)
el.textContent = settings.text; //el.innerText; For IE
return el;
//Usage:
$(function()
$("#svg-elem").append(
createSvgElem(tagName: "rect", width: 130, height: 500, style: "fill: #000000a3;")
)
$("#svg-elem").append(
createSvgElem(tagName: "text", x: 30, y: 30, text: "ABCD", style: "fill: red")
)
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<svg id="svg-elem" >
</svg>
【讨论】:
【参考方案15】:一种更简单的方法是将 SVG 生成为一个字符串,创建一个包装 HTML 元素,然后使用 $("#wrapperElement").html(svgString)
将 svg 字符串插入到 HTML 元素中。这在 Chrome 和 Firefox 中运行良好。
【讨论】:
以上是关于jquery append 不适用于 svg 元素?的主要内容,如果未能解决你的问题,请参考以下文章
Firefox 错误 - globalCompositeOperation 不适用于 SVG 元素的 drawImage