jquery追加不使用svg元素?

时间:2010-09-04 11:24:18

标签: jquery html5 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>

为什么我什么都看不到?

15 个答案:

答案 0 :(得分:235)

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

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

HTML5承诺将来允许您在纯HTML(<svg>)文档中使用xmlns而不使用text/html。但这只是一个解析器hack(**),SVG内容仍然是SVG名称空间中的SVGElements,而不是HTMLElements,所以即使他们看起来<<1},你也无法使用innerHTML / em>喜欢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,但您可以使用innerHTML将新的SVGE元素注入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-width="2" fill="red" onmousedown="alert(\'hello\');"/>'
    ));
]]></script>

**:我讨厌HTML5的作者似乎害怕XML的方式,并决心将基于XML的功能扼杀到HTML这个棘手的混乱中。 XHTML在几年前解决了这些问题。

答案 1 :(得分:137)

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" width="200px" height="100px">
    </svg>
</div>

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

这项技术的优点:

  • 您可以编辑现有的svg(已经在DOM中),例如。使用Raphael创建或在您的示例中使用“硬编码”而无需编写脚本。
  • 您可以将复杂的元素结构添加为字符串,例如。像在jQuery中一样$('svg').prepend('<defs><marker></marker><mask></mask></defs>');
  • 添加元素并使用$("#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时不起作用。这将是非常简短的黑客攻击。)

答案 2 :(得分:36)

越来越受欢迎的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");

答案 3 :(得分:25)

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/

答案 4 :(得分:14)

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

您可以使用vanilla Javascript创建元素作为具有正确名称空间的普通DOM节点,然后使用jQuery-ify来创建元素。像这样:

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元素,否则它将无法工作。

答案 5 :(得分:9)

找到了一种适用于我所有浏览器的简单方法(Chrome 49,Edge 25,Firefox 44,IE11,Safari 5 [Win],Safari 8(MacOS)):

&#13;
&#13;
.svgStyle
{
  fill:cornflowerblue;
  fill-opacity:0.2;
  stroke-width:2;
  stroke-dasharray:5,5;
  stroke:black;
}
&#13;
<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>
&#13;
perl
&#13;
&#13;
&#13;

答案 6 :(得分:8)

我可以在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>

答案 7 :(得分: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);

答案 8 :(得分: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))

答案 9 :(得分:3)

Bobince接受的答案是一个简短的便携式解决方案。如果您不仅需要附加SVG而且还要操纵它,您可以尝试JavaScript library "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');

答案 10 :(得分:1)

我建议使用ajax并从另一页加载svg元素会更好。

$('.container').load(href + ' .svg_element');

其中href是带有svg的页面的位置。这样,您可以避免替换html内容可能引起的任何抖动效果。另外,别忘了在svg加载后将其解开:

$('.svg_element').unwrap();

答案 11 :(得分: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");
}

制作:

this SVG image as seen in Firefox 57

答案 12 :(得分: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.

答案 13 :(得分:0)

我为此做了一个小功能。对于 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" width="200" height="200">
</svg>

答案 14 :(得分:-1)

更简单的方法是将SVG生成为字符串,创建包装HTML元素并使用$("#wrapperElement").html(svgString)将svg字符串插入HTML元素。这在Chrome和Firefox中运行良好。