如何将SVG图像动态插入HTML?

时间:2011-11-02 13:25:44

标签: javascript html ajax svg

我有一些代码可以通过Ajax从服务器检索脚本化的svg映像。我可以将图像文本返回到浏览器中,但我找不到将其插入实际显示它的DOM的方法。有人能帮忙吗? svg看起来像这样:

<svg id="chart" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" onload="init(evt)">
<script type="application/ecmascript">
<![CDATA[
...lots of code, changes on each Ajax request
//]]>
</script>
<script type="application/ecmascript" xlink:href="js-on-server-1.js"/>
<script type="application/ecmascript" xlink:href="js-on-server-2.js"/>
</svg>

我尝试过各种各样的事情。如果我这样做:

// xmlhttp.onreadystatechange:
addImage(xmlhttp.responseXML, "somewhere");
...
function addImage(txt, dst_id) {
   var scr = document.createElement("div");

   if("textContent" in scr)
      scr.textContent = txt;  // everybody else
   else
      scr.text = txt;         // IE

   document.getElementById(dst_id).appendChild(scr);
}

然后Opera和Chrome什么都不做,F / F抱怨“[object XMLDocument]”。如果我将'responseXML'更改为'responseText',那么Opera / Chrome会在正确的位置正确显示整个svg文本(而不是图像),而F / F仍然会发出相同的警告。 我也尝试将响应分配给innerHTML,但这没有任何作用。 有任何想法吗?感谢。

修改

回应下面的Phrogz'z回答 - 我添加了两个简单的svg文件。第一个是'标准'简单svg,显示一个圆圈。第二个是脚本svg,显示一个矩形。您应该能够在任何浏览器中直接查看它们,IE8-除外。 如果我编辑Phrogz'z代码以使用圆形文件(将'stirling4.svg'替换为此文件的名称),那么它可以工作,但如果我想要脚本矩形,则不会。在F / F,Opera,Chromium上测试过,但在(我的)Chromium上无论如何都没有。

文件1,圈子:

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red" />
</svg>

文件2,矩形:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" onload="init(evt)">
<script type="application/ecmascript">
<![CDATA[
var svgDocument;
var svgns = "http://www.w3.org/2000/svg";
function init(evt) {
  if(window.svgDocument == null)
    svgDocument = evt.target.ownerDocument;
   var lbox = svgDocument.createElementNS(svgns, "rect");
   lbox.setAttributeNS(null, "x",                10);
   lbox.setAttributeNS(null, "y",                10);
   lbox.setAttributeNS(null, "width",            30);
   lbox.setAttributeNS(null, "height",           30);
   lbox.setAttributeNS(null, "stroke",           "#8080ff");
   lbox.setAttributeNS(null, "stroke-width",     2);
   lbox.setAttributeNS(null, "fill-opacity",     0);
   lbox.setAttributeNS(null, "stroke-opacity",   1);
   lbox.setAttributeNS(null, "stroke-dasharray", 0);
   svgDocument.documentElement.appendChild(lbox);
}
//]]>
</script>
</svg>

据推测,答案是将脚本放入标题?

1 个答案:

答案 0 :(得分:23)

一般来说,问题是 twofold 三倍:

  1. HTML不是XHTML,在撰写本文时,对HTML中的SVG的支持是伪劣的,定义不明确。解决方案是使用真正的XHTML文档,其中SVG命名空间元素实际上被视为SVG。

  2. responseXML位于另一个DOM文档中,通常只能将节点从一个文档移动到另一个文档。您应该使用document.importNode将节点从一个文档导入另一个文档。

  3. 使用onload事件处理程序加载SVG文件将不会通过创建节点或将其附加到文档来调用这些处理程序。但是,script块中的代码将被运行,因此您需要以独立的方式和动态加载的方式重写脚本。


  4. 这是一个适用于Chrome,Safari和Firefox的简单示例...但不适用于IE9:

    var xhr = new XMLHttpRequest;
    xhr.open('get','stirling4.svg',true);
    xhr.onreadystatechange = function(){
      if (xhr.readyState != 4) return;
      var svg = xhr.responseXML.documentElement;
      svg = document.importNode(svg,true); // surprisingly optional in these browsers
      document.body.appendChild(svg);
    };
    xhr.send();
    

    在此处查看此行动:http://phrogz.net/SVG/import_svg.xhtml


    不幸的是IE9没有正确支持document.importNode。为了解决这个问题,我们编写了自己的cloneToDoc函数,通过递归爬行层次结构为任何给定节点创建了一个等效结构。这是一个完整的工作示例:

    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head> 
      <meta http-equiv="content-type" content="application/xhtml+xml;charset=utf-8"/>
      <title>Fetch and Include SVG in XHTML</title>
      <script type="text/ecmascript"><![CDATA[
        setTimeout(function(){
          var xhr = new XMLHttpRequest;
          xhr.open('get','stirling4.svg',true);
          xhr.onreadystatechange = function(){
            if (xhr.readyState != 4) return;
            var svg = cloneToDoc(xhr.responseXML.documentElement);
            document.body.appendChild(svg);
          };
          xhr.send();
        },1000);
        function cloneToDoc(node,doc){
          if (!doc) doc=document;
          var clone = doc.createElementNS(node.namespaceURI,node.nodeName);
          for (var i=0,len=node.attributes.length;i<len;++i){
            var a = node.attributes[i];
            if (/^xmlns\b/.test(a.nodeName)) continue; // IE can't create these
            clone.setAttributeNS(a.namespaceURI,a.nodeName,a.nodeValue);
          }
          for (var i=0,len=node.childNodes.length;i<len;++i){
            var c = node.childNodes[i];
            clone.insertBefore(
              c.nodeType==1 ? cloneToDoc(c,doc) : doc.createTextNode(c.nodeValue),
              null
            ); }
          return clone;
        }
      ]]></script>
    </head><body></body></html>
    

    在此处查看此行动:http://phrogz.net/SVG/import_svg_ie9.xhtml


    编辑2:正如所怀疑的那样,问题是动态添加脚本时onload事件不会触发。这是一个有效的配对解决方案:

    1. 重写您的脚本以删除onload事件处理程序。相反,相信document存在。
    2. 重写您的脚本以请求全局svgRoot;如果不存在,请使用document.documentElement
    3. 获取SVG后,在将其导入文档后,将全局svgRoot设置为新的svg元素。
    4. 以下是行动中的代码:

      并且,如果我的网站关闭,这里是后代的代码:

      <强>脚本created.svg

      <svg xmlns="http://www.w3.org/2000/svg">
        <script type="text/javascript"><![CDATA[
          function createOn( root, name, a ){
            var el = document.createElementNS(svgNS,name);
            for (var n in a) if (a.hasOwnProperty(n)) el.setAttribute(n,a[n]);
            return root.appendChild(el);
          }
          // Trust someone else for the root, in case we're being
          // imported into another document
          if (!window.svgRoot) svgRoot=document.documentElement;
          var svgNS = svgRoot.namespaceURI;
          createOn(svgRoot,'rect',{
            x:10, y:10, width:30, height:30,
            stroke:'#8080ff', "stroke-width":5,
            fill:"none"
          });
        ]]></script>
      </svg>
      

      <强> import_svg_with_script.xhtml

      <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head> 
        <meta http-equiv="content-type"
              content="application/xhtml+xml;charset=utf-8" />
        <title>Fetch and Include Scripted SVG in XHTML</title>
        <script type="text/ecmascript"><![CDATA[
          setTimeout(function(){
            var xhr = new XMLHttpRequest;
            xhr.open('get','script-created.svg',true);
            xhr.onreadystatechange = function(){
              if (xhr.readyState != 4) return;
              var svg = xhr.responseXML.documentElement;
              svg = cloneToDoc(svg);
              window.svgRoot = svg; // For reference by scripts
              document.body.appendChild(svg);
              delete window.svgRoot;
            };
            xhr.send();
          },1000);
          function cloneToDoc(node,doc){
            if (!doc) doc=document;
            var clone = doc.createElementNS(node.namespaceURI,node.nodeName);
            for (var i=0,len=node.attributes.length;i<len;++i){
              var a = node.attributes[i];
              if (/^xmlns\b/.test(a.nodeName)) continue; // IE can't create these
              clone.setAttributeNS(a.namespaceURI,a.nodeName,a.nodeValue);
            }
            for (var i=0,len=node.childNodes.length;i<len;++i){
              var c = node.childNodes[i];
              clone.insertBefore(
                c.nodeType==1 ? cloneToDoc(c,doc) : doc.createTextNode(c.nodeValue),
                null
              )
            }
            return clone;
          }
        ]]></script>
      </head><body></body></html>