如何将 SVG 图像动态插入 HTML? [英] How do I dynamically insert an SVG image into HTML?

查看:27
本文介绍了如何将 SVG 图像动态插入 HTML?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have some code that retrieves a scripted svg image from a server via Ajax. I can get the image text back into the browser, but I can't find a way to insert it into the DOM that will actually display it. Can anyone help with this? The svg looks likes this:

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

我尝试了各种方法.如果我这样做:

I've tried various things. If I do this:

// 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,但这没有任何作用.有任何想法吗?谢谢.

Then Opera and Chrome do nothing, and F/F complains "[object XMLDocument]". If I change 'responseXML' to 'responseText', then Opera/Chrome correctly display the entire svg text (not image) in the right place, and F/F still gives the same warning. I've also tried assigning the response to an innerHTML, but that does nothing. Any ideas? Thanks.

编辑

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

In response to Phrogz'z answer below - I've added two simple svg files. The first is a 'standard' simple svg, displaying a circle. The second is a scripted svg, displaying a rectangle. You should be able to view both directly in any browser, except IE8-. If I edit Phrogz'z code to use the circle file (replace 'stirling4.svg' with the name of this file), then it works, but if I want the scripted rectangle instead, it doesn't. Tested on F/F, Opera, Chromium, but doesn't work anyway on (my) 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>

想必答案是将脚本放入标题中??

Presumably the answer is to get the script into the header??

推荐答案

总的来说,问题是twofold三重的:

In general, the problem is twofold threefold:

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

  1. HTML is not XHTML, and support for SVG in HTML is shoddy and poorly-defined as of this writing. The solution is to use a real XHTML document where SVG-namespaced elements are actually treated as SVG.

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

The responseXML is in another DOM document, and you can't normally just move nodes from one document to another. You are supposed to use document.importNode to import a node from one document to another.

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

Loading an SVG file with onload event handlers will not have those handlers invoked by either creating the node or appending it to the document. Code inside the script block will be run, however, so you need to rewrite your scripts in a manner that works standalone and also with the dynamic loading.

<小时>

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


Here's a simple example that works in Chrome, Safari, and Firefox...but not 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

See it in action here: http://phrogz.net/SVG/import_svg.xhtml

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

Unfortunately IE9 does not properly support document.importNode. To work around this, we write our own cloneToDoc function that creates an equivalent structure for any given node by recursively crawling the hierarchy. Here's a full working example:

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

See it in action here: http://phrogz.net/SVG/import_svg_ie9.xhtml

编辑 2: 怀疑,问题是动态添加脚本时 onload 事件没有触发.这是一个有效的配对解决方案:

Edit 2: As suspected, the problem is that the onload event does not fire when dynamically adding script. Here's a paired solution that works:

  1. 重写您的脚本以移除 onload 事件处理程序.相反,请相信 document 存在.
  2. 重写您的脚本以请求全局svgRoot;如果它不存在,请使用 document.documentElement.
  3. 获取 SVG 时,将全局 svgRoot 设置为新的 svg 元素,然后将其导入文档.
  1. Rewrite your script to remove the onload event handler. Instead, trust that document exists.
  2. Rewrite your script to ask for a global svgRoot; if it doesn't exist, use document.documentElement.
  3. When fetching the SVG set a global svgRoot to the new svg element after you import it into the document.

这是正在运行的代码:

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

And, in case my site is down, here is the code for posterity:

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

这篇关于如何将 SVG 图像动态插入 HTML?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆