SVG/XML 中是否有一些innerHTML 替换?

IT技术 javascript xml templates svg
2021-02-22 16:31:46

在 HTML 中,我可以通过提供一个字符串形式的模板来构建一个简单的模板系统,替换它的某些部分,然后将其分配innerHTML给某个容器。

var templ = '<span>{myText}</span>'
var newContent = templ.replace( '{myText}', someVariable );
document.querySelector( '#myContainer' ).innerHTML = newContent;

这样我就可以利用浏览器的 HTML 解析器,而不必重复使用document.createElement(). 如果模板超出几个元素,后者可能会非常麻烦。

然而,在 SVG 中,元素本身没有属性,innerHTML甚至没有属性innerSVG

所以我的问题是:有什么我可以在 SVG ro 中使用的类似于上面示例中的方法还是我坚持使用document.createElement()(或分别使用它的一些库)?

和我的问题一样:Vanilla JavaScript 解决方案是首选,但任何指向提供解决方案的 lib 的指针都值得赞赏。

6个回答

您可以使用DOMParser来解析 XML。然后,如果您希望通过importNode以这样的方式结束,您可以使用 importNode 将其放入现有文档中...

var doc = new DOMParser().parseFromString(
   '<svg xmlns="http://www.w3.org/2000/svg"><circle cx="100" cy="100" r="20"/></svg>',
   'application/xml');

someElement.appendChild(
 someElement.ownerDocument.importNode(doc.documentElement, true));
是的,片段需要一个命名空间属性。
2021-04-16 16:31:46
这要求片段是有效的 XML 文档(仅限单根),对吗?对于不是由具有正确命名空间的 SVG 元素作为父级的片段,创建的元素是否具有正确的命名空间?
2021-04-22 16:31:46
您可能想要/需要将其包装为一个函数,该函数将字符串包装在 SVG 根中,并预定义了所有常见的命名空间和前缀,然后提取内容。
2021-05-14 16:31:46

查看innerSVG javascript shim,它提供了您想要的功能。

2014更新:DOM解析规范定义innerHTMLouterHTMLElement,这使得这些可用对SVG和XML元素。这已经在 Blink 中发布了一段时间,支持它的第一个版本是 Chrome 32 / Opera 19,更多细节可以在这个错误报告中找到

@nroose 是的,有可能,请参阅xn--dahlstrm-t4a.net/svg/html/...如果你不能让它工作,请发布一个新问题。
2021-04-21 16:31:46
嘿,你知道有没有办法让innerSVG 与<object> 中的SVG 一起工作?似乎它只适用于内联 svg,而我们的东西目前只适用于 <object> 中的 SVG...谢谢。
2021-05-04 16:31:46

在这里我写了一个肮脏的方式......

SVG 的innerHTML 解决方法

http://jsfiddle.net/microbians/8ztNU/

<html>
    <body>
        <svg id="svgcanvas">
        </svg>
        <script>
            var twocircles='<circle cx="253" cy="562" r="10" stroke="black" stroke-width="2" fill="red"></circle> \
            <circle cx="353" cy="562" r="10" stroke="black" stroke-width="2" fill="red"></circle>'
            var receptacle = document.createElement('div');
            var svgfragment='<svg>'+twocircles+'</svg>';
            receptacle.innerHTML=''+svgfragment;
            var Nodes=Array.prototype.slice.call(receptacle.childNodes[0].childNodes);
            Nodes.forEach(function(el){document.getElementById('svgcanvas').appendChild(el)})
        </script>
    </body>
</html>

享受!

这是行不通的!我已经用 IE10(从 2012 年开始)和当前的 Opera 版本对其进行了测试。你有没有测试过?
2021-04-19 16:31:46

我的innerSVG垫片怎么样?CoffeeScript 源代码如下,编译后的 JavaScript 位于https://gist.github.com/2648095

# Important: You must serve your pages as XHTML for this shim to work,
# otherwise namespaced attributes and elements will get messed up.
Object.defineProperty SVGElement.prototype, 'innerHTML',
  get: () ->
    $temp = document.createElement 'div'
    $node = @cloneNode true

    for $child in $node.children
      $temp.appendChild $child

    return $temp.innerHTML

  set: (markup) ->
    while @firstChild
      @firstChild.parentNode.removeChild @firstChild

    markup = "<svg id='wrapper' xmlns='http://www.w3.org/2000/svg'>#{markup}</svg>"
    $div = document.createElement 'div'
    $div.innerHTML = markup
    $svg = $div.querySelector 'svg#wrapper'

    for $element in $svg.children
      @appendChild $element 
  enumerable : false  
  configurable : true

简短的回答是“不,在 XML 的世界中没有任何等价物可以让您对其进行一些标记,并让它在您插入的位置的正确命名空间中自动创建所有元素和属性。”

最接近的直接答案是@Robert 所拥有的。正如我在评论中所指出的,即使这样,您也需要在 SVG 文档中创建任何片段,这些片段与要插入片段的文档具有相同的命名空间和前缀。

相反,您可能会发现在标准 DOM 方法上使用便利方法同样容易(或更容易):

// Create a named SVG element on a node, with attributes and optional text
function appendTo(node,name,attrs,text){
  var p,ns=appendTo.ns,svg=node,doc=node.ownerDocument;
  if (!ns){ // cache namespaces by prefix once
    while (svg&&svg.tagName!='svg') svg=svg.parentNode;
    ns=appendTo.ns={svg:svg.namespaceURI};
    for (var a=svg.attributes,i=a.length;i--;){
      if (a[i].namespaceURI) ns[a[i].localName]=a[i].nodeValue;
    }
  }
  var el = doc.createElementNS(ns.svg,name);
  for (var attr in attrs){
    if (!attrs.hasOwnProperty(attr)) continue;
    if (!(p=attr.split(':'))[1]) el.setAttribute(attr,attrs[attr]);
    else el.setAttributeNS(ns[p[0]]||null,p[1],attrs[attr]);
  }
  if (text) el.appendChild(doc.createTextNode(text));
  return node.appendChild(el);
}

function clear(node){
  while (node.lastChild) node.removeChild(node.lastChild);
}

有了它,您可以执行以下操作:

var icons={
  Apps  : "/images/apps.png",
  Games : "/images/games.png"
}
var wrap = document.querySelector('#container');
clear(wrap);

for (var label in icons){
  if (!icons.hasOwnProperty(label)) continue;
  var icon = appendTo(wrap,'g',{'class':'icon'});
  appendTo(icon,'image',{'xlink:href':icons[label]});
  appendTo(icon,'text',{x:10,y:20},label);
}

恕我直言,这比尝试使用字符串连接构建原始 SVG 标记更干净:

var svg = [];
for (var label in icons){
  if (!icons.hasOwnProperty(label)) continue;
  svg.push('<g class="icon">');
  svg.push('<image xlink:href="'+icons[label]+'" />');
  svg.push('<text x="10" y="20">'+label+'</text>');
  svg.push('</g>');
}
wrap.innerSVG = svg.join(''); // doesn't work, of course