javascript - 如何将 SVG 图像动态插入到 HTML 中?

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

大概答案是把脚本放到header里??

最佳答案

一般来说,问题是双重三重的:

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

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

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


这是一个适用于 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 元素,然后将其导入文档。

下面是运行中的代码:

而且,如果我的网站出现故障,这里是后代的代码:

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

关于javascript - 如何将 SVG 图像动态插入到 HTML 中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7981100/

相关文章:

javascript - 基本名称无法连接 react 路由器

javascript - 在 JavaScript 中重命名返回 "EPERM: operation not permitted"

javascript - 我如何在 codemirror 的模式中以特定颜色对评论进行着色

javascript - 如何将 php 变量传递给 Wordpress AJAX 调用?

javascript - 使用页面子元素测量页面下载时间和渲染时间

javascript - 使用 Javascript 过滤从 PHP 返回的项目

javascript - 行删除不起作用

html - 将 div 放置在不同宽度的图像内

html - 在 HTML 中修复表内表的表宽

javascript - RoR无需刷新召回功能