【问题标题】:jquery's append not working with svg element?jquery append 不适用于 svg 元素?
【发布时间】:2021-02-07 01:01:36
【问题描述】:

假设:

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

为什么我什么都看不到?

【问题讨论】:

    标签: jquery html svg


    【解决方案1】:

    当您将标记字符串传递到$ 时,它会使用浏览器在&lt;div&gt; 上的innerHTML 属性解析为HTML(或其他适合特殊情况的容器,例如&lt;tr&gt;)。 innerHTML 无法解析 SVG 或其他非 HTML 内容,即使可以解析,它也无法判断 &lt;circle&gt; 应该在 SVG 命名空间中。

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

    HTML5 承诺将来让您在纯 HTML (text/html) 文档中使用不带 xmlns&lt;svg&gt;。但这只是一个解析器 hack(**),SVG 内容仍将是 SVG 命名空间中的 SVGElements,而不是 HTMLElements,因此您将无法使用 innerHTML,即使它们看起来 就像 HTML 文档的一部分。

    但是,对于当今的浏览器,您必须使用 XHTML(正确地用作 application/xhtml+xml;保存为 .xhtml 文件扩展名以进行本地测试)才能使 SVG 正常工作。 (无论如何,这有点道理;SVG 是一个适当的基于 XML 的标准。)这意味着您必须在脚本块中转义 &lt; 符号(或包含在 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-width="2" 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" width="200px" height="100px">
        </svg>
    </div>
    

    函数示例:
    http://jsbin.com/ejifab/1/edit

    这种技术的优点:

    • 您可以编辑现有的 svg(已经在 DOM 中),例如。在您的示例中使用 Raphael 或类似“硬编码”创建,无需编写脚本。
    • 您可以将复杂的元素结构添加为字符串,例如。 $('svg').prepend('&lt;defs&gt;&lt;marker&gt;&lt;/marker&gt;&lt;mask&gt;&lt;/mask&gt;&lt;/defs&gt;'); 就像你在 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 文档,虚拟对象可能类似于 &lt;svg id="dummy" style="display:none"&gt;&lt;defs&gt;&lt;filter&gt;&lt;!-- Filter definitons --&gt;&lt;/filter&gt;&lt;/defs&gt;&lt;/svg&gt;。文本表示首先使用 jQuery 的 $("body").append() 方法转换为 DOM。当 (filter) 元素在 DOM 中时,可以使用标准 jQuery 方法对其进行查询,并将其附加到由 Raphael 创建的主 SVG 文档中。

    为什么需要这个假人?为什么不对 Raphael 创建的文档严格添加过滤器元素?如果您尝试使用例如。 $("svg").append("&lt;circle ... /&gt;"),它被创建为 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 无法将元素附加到 &lt;svg&gt;(它似乎确实将它们添加到 DOM 资源管理器中,但不是在屏幕上)。

    一种解决方法是将&lt;svg&gt; 与您需要的所有元素附加到页面,然后使用.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())单独附加 &lt;circle&gt; 或其他元素,而是将整个 svg 文档附加到容器中。而不是 $('body') 它当然也可以是一些 $('div') 。之后,svg 文档位于 DOM 上,可以使用例如熟悉的方式进行查询。 $('c').attr('fill','red').
    • 我又做了一个例子:jsbin.com/inevaz/1。它从 Internet 获取tiger.svg 源代码到 iframe,并且可以复制粘贴到 textarea。然后将 textarea 的内容用作内联 svg 的源,然后可以通过 DOM 访问(以更改笔触样式)。
    • 这太好了,我不知道为什么每个人都在投票支持那些根本不起作用的其他答案。
    • 这对我来说也是最好的答案。我需要一个带有 的 svg,这是最短的工作答案。
    【解决方案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 中无效。这个答案解决了两个浏览器的问题!
    • 不错!很高兴知道
    • 使用 jquery .attr({}) 更简单,tks
    【解决方案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" height="100" width="200"></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('&lt;circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red"/&gt;'));
    【解决方案9】:

    如果您需要附加的字符串是 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))
    

    【讨论】:

      【解决方案10】:

      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');
      

      【讨论】:

        【解决方案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" width="200" height="200">
            </svg>

            【讨论】:

              【解决方案15】:

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

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2018-03-26
                • 2012-01-12
                • 1970-01-01
                • 2022-01-21
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多