【问题标题】:Is there some innerHTML replacement in SVG/XML?SVG/XML 中是否有一些 innerHTML 替换?
【发布时间】:2012-04-01 04:17:40
【问题描述】:

在 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 解决方案是首选,但任何指向提供解决方案的库的指针都值得赞赏。

【问题讨论】:

    标签: javascript xml templates svg


    【解决方案1】:

    您可以使用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));
    

    【讨论】:

    • 这要求片段是有效的 XML 文档(仅限单根),对吗?对于不是由具有正确命名空间的 SVG 元素作为父级的片段,创建的元素是否具有正确的命名空间?
    • 您可能希望/需要将其包装为一个函数,该函数将字符串包装在 SVG 根目录中,并预定义所有常见的命名空间和前缀,然后提取内容。
    • 是的,片段需要命名空间属性。
    【解决方案2】:

    更简单地说,document.querySelector('#myContainer').textContent = newContent; 有很好的支持,def。到 IE9+、Safari、FF、Chrome。

    Mike Bostock 是我解决这类问题的英雄:D3.js source code 是我解答 SVG 问题的首选。

    【讨论】:

    • innerHTML 有一些功能不仅仅是设置节点的文本值,而是触发 HTML 解析。所以我认为我无法使用textContent 添加新节点。
    • 好收获。是的,我通常使用document.createElement(...),但可以编写一个处理它的函数。
    【解决方案3】:

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

    2014 年更新: DOM parsing specElement 上定义了 innerHTMLouterHTML,这使得它们可用于 svg 和 xml 元素。这已经在 Blink 中发布了一段时间,第一个支持它的版本是 Chrome 32 / Opera 19,更多细节可以在this bugreport 找到。

    【讨论】:

    • 嘿,你知道是否有办法让innerSVG 与 中的SVG 一起工作?似乎它只适用于内联 svg,而我们的东西目前只适用于 中的 SVG...谢谢。
    • @nroose 是的,有可能,请参阅xn--dahlstrm-t4a.net/svg/html/…。如果你不能让它工作,请发布一个新问题。
    【解决方案4】:

    这里我写个脏办法……

    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 版本对其进行了测试。你测试过吗?
    【解决方案5】:

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

    【讨论】:

      【解决方案6】:

      看起来至少在 Chrome 和 Safari 上,您可以将 SVG 元素包装在 HTML 元素中并请求 innerHTML。以下 HTML 文件呈现红色矩形,即使 SVG 源指定绿色。

      <body>
      <div id="wrapper">
        <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="120" height="120" viewBox="0 0 236 120">
          <rect x="14" y="23" width="250" height="50" fill="green" stroke="black" stroke-width="1" />
        </svg>
      </div>
      <script>
        var el = document.getElementById('wrapper');
        var t = el.innerHTML;
        t = t.replace(/green/g, "red");
        el.innerHTML = t;
      </script>
      </body>
      

      【讨论】:

        【解决方案7】:

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

        最接近的直接答案是@Robert 所拥有的。正如我的 cmets 中所述,即使那样,您也需要在 SVG 文档中创建任何 sn-ps,该文档与您将插入片段的文档具有相同的命名空间和前缀。

        相反,您可能会发现在标准 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
        

        【讨论】:

          【解决方案8】:

          使用 jQuery,你可以这样做:

          假设您的svgString 在替换操作后包含您的 svg 图像。

          $(svgString)[0] 创建与您的字符串对应的 svg 标签。然后,您可以将此元素附加到 dom 中您想要绘制图像的位置。

          希望对你有帮助

          【讨论】:

          • 您确定这会在正确的命名空间中创建元素吗?
          猜你喜欢
          • 1970-01-01
          • 2012-08-14
          • 2020-11-26
          • 1970-01-01
          • 2014-03-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多