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>

为什么我什么都没看到?

原文由 1521237 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 476
2 个回答

当您将标记字符串传递给 $ 时,它会使用浏览器的 innerHTML 属性解析为 <tr> 上的属性 <div> --- )。 innerHTML 无法解析 SVG 或其他非 HTML 内容,即使可以,也无法判断 <circle> 应该在 SVG 命名空间中。

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

HTML5 承诺让您将来在纯 HTML ( text/html ) 文档中使用 <svg> 而无需 xmlns 。但这只是一个解析器 hack(**),SVG 内容仍然是 SVG 命名空间中的 SVGElements,而不是 HTMLElements,因此您将无法使用 innerHTML 即使它们 看起来 像一部分的 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 将新的 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-width="2" fill="red" onmousedown="alert(\'hello\');"/>'
    ));
]]></script>

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

原文由 bobince 发布,翻译遵循 CC BY-SA 2.5 许可协议

接受的答案 显示太复杂的方式。正如 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>

功能示例:

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

原文由 Timo Kähkönen 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题