包括带有HTML的SVG文件,并且仍然能够向它们应用样式? [英] Include SVG files with HTML, and still be able to apply styles to them?

查看:164
本文介绍了包括带有HTML的SVG文件,并且仍然能够向它们应用样式?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

您可以通过 embed object svg 标签。




  • 使用嵌入 $ c> object 标记需要将图像与URL关联。 (这是我喜欢的,因为我不喜欢我的HTML代码中的所有SVG代码,所以我想保持这样。)

  • 使用 svg 标记(AFAIK)要求您的HTML代码中包含SVG代码。



问题是:



如何将SVG图标,图片和其他文件添加到我的HTML文件中,而不必将整个SVG代码放入,能够向他们应用样式吗?



注意:



当我通过 object embed ,我似乎不能通过jQuery访问它们,即使使用 $ #my-svg-div)。find(svg)(顺便说一句,几乎每一个SO的答案都说我应该)。我只是得到 undefined



谢谢!

解决方案

更新:添加了一个替代策略,也允许在< img> 元素中处理SVG图像,



短回答



可以以编程方式内联SVG图像。这样的图像可以基本上与实际的内联< svg> 元素相同地处理,包括能够对其应用样式。



如果您的SVG图像在< object> < iframe> 元素( e ),您可以按如下方式内联:



e.parentElement.replaceChild (e.contentDocument.documentElement.cloneNode(true),e);



如果您的SVG图片在< embed> 元素,在上面的代码中用 .getSVGDocument()替换 .contentDocument



如果您的SVG图像在< img> 元素中引用,那么可以使用涉及AJAX和下面描述的完全不同的策略



总体策略



如果您的外部SVG图像文件是同源的(例如图像从与HTML代码相同的位置加载),则允许这些图像的样式的一种方法是以编程方式将它们内联,如下:





  • 将此内容直接添加到与原始引用元素相同的HTML DOM位置中的HTML文件,即将其放置为内嵌。

  • 删除最初引用外部SVG文件的元素。





此内联策略为您提供两个世界的最佳方式:


  1. 包括




    • 独立于HTML组织图像文件,

    • 保持原始HTML文件与图片详细信息不重叠,

    • (可能)允许浏览器缓存图片(但有关最后一点,请参阅下文)。


  2. 但是,您仍然可以对最终内联的SVG图片执行任何操作,您可以对< svg> 元素,这些元素最初是内联的,包括:




    • 将CSS样式应用到它们,





实施 / h1>

< object> < iframe> 元素:



您可以如下所示内置外部引用的SVG代码:

  // using vanilla JavaScript(如上所示):
e.parentElement.replaceChild(e.contentDocument.documentElement.cloneNode(true),e);

//使用jQuery:
$ e.replaceWith($($ e [0] .contentDocument.documentElement).clone());

...其中 e $ e 分别是vanilla或jQuery变量,其中你选择了外部SVG引用< object> < iframe> 元素。



包括< embed> 元素:



如果使用外部SVG引用< embed> 元素,那么您可以通过用 .getSVGDocument()()替换上述代码中的 .contentDocument 注意额外的括号)。请注意, .contentDocument 不能与< embed> 元素, .getSVGDocument 实际上适用于所有三种元素类型。但 .getSVGDocument()



点击按钮后,尝试内联,屏幕现在看起来像这样。 CSS样式已成功应用于某些元素:





此示例所需的代码如下:



image.svg (外部参考文件,即未嵌入HTML):

 < svg xmlns =http: /www.w3.org/2000/svgwidth =50pxheight =50px> 
< polygon points =25,5 45,45 5,45 25,5/>
< / svg>

index.html strong>(显然,如果不使用jQuery,删除jQuery脚本行):

 <!DOCTYPE html> 
< head>
< link href =styles.css =stylesheet>
< script src =// code.jquery.com/jquery-1.12.0.min.js\"> ;</script>
< script src =main.js>< / script>
< / head>
< body>
< button>点击尝试内嵌svg图片。< / button>
< table>
< tr>
< th>< / th>
< th> svg< / th>
< th> object< / th>
< th> iframe< / th>
< th> embed< / th>
< th> img< / th>
< / tr>
< tr>
< td> contentDocument< / td>
< td>< svg xmlns =http://www.w3.org/2000/svgwidth =50height =50>< polygon points =25,5 45 ,45 5,45 25,5/>< / svg>< / td>
< td>< object data =image.svgtype =image / svg + xml>< / object>< / td>
< td>< iframe src =image.svgwidth =50height =50style =border:none;>< / iframe>< / td>
< td>< embed src =image.svgtype =image / svg + xml/>< / td>
< td>< img src =image.svg/>< / td>
< / tr>
< tr>
< td> getSVGDocument()< br />(已弃用)< / td>
< td>< svg xmlns =http://www.w3.org/2000/svgwidth =50height =50>< polygon points =25,5 45 ,45 5,45 25,5/>< / svg>< / td>
< td>< object data =image.svgtype =image / svg + xml>< / object>< / td>
< td>< iframe src =image.svgwidth =50height =50style =border:none;>< / iframe>< / td>
< td>< embed src =image.svgtype =image / svg + xml/>< / td>
< td>< img src =image.svg/>< / td>
< / tr>
< tr>
< td> XMLHttpRequest< /td>
< td>< svg xmlns =http://www.w3.org/2000/svgwidth =50height =50>< polygon points =25,5 45 ,45 5,45 25,5/>< / svg>< / td>
< td>< object data =image.svgtype =image / svg + xml>< / object>< / td>
< td>< iframe src =image.svgwidth =50height =50style =border:none;>< / iframe>< / td>
< td>< embed src =image.svgtype =image / svg + xml/>< / td>
< td>< img src =image.svg/>< / td>
< / tr>
< / table>
< / body>
< / html>

styles.css strong>(只有 polygon 行对于演示内联很重要):

  polygon {fill:red;} 
table {border-collapse:collapse;}
td,th {border:solid black 1px; padding:0.4em;}

main.js (jQuery版本):

  $ b $(button)。click(function(){
[object,iframe,embed,img]。forEach(function(elmtType){
var $ e,$ threeElmts = $(elmtType);
$ e = $($ threeElmts [0]);
if($ e [0] .contentDocument)$ e.replaceWith($($ e [ 0] .contentDocument.documentElement).clone());
$ e = $($ threeElmts [1]);
if($ e [0] .getSVGDocument)$ e.replaceWith $ e [0] .getSVGDocument()。documentElement).clone());
$ e = $($ threeElmts [2]);
$ .get($ e.attr prop(nodeName)===OBJECT?data:src),function(data){
$ e.replaceWith(data.documentElement);
});
});
});
});

main.js strong>(vanilla JavaScript版本):

  document.addEventListener(DOMContentLoaded,function(){
document。 querySelector(button)。addEventListener(click,function(){
[object,iframe,embed,img]。forEach(function(elmtType){
var e,threeElmts = document.querySelectorAll(elmtType);
e = threeElmts [0];
if(e.contentDocument)e.parentElement.replaceChild(e.contentDocument.documentElement.cloneNode );
e = threeElmts [1];
if(e.getSVGDocument)e.parentElement.replaceChild(e.getSVGDocument()。documentElement.cloneNode(true),e);
e = threeElmts [2];
var xhr = new XMLHttpRequest();
xhr.open(GET,e.getAttribute(e.nodeName ===OBJECT?data:src) );
xhr.send();
xhr.onreadystatechange = function(){
if(xhr.readyState === XMLHttpRequest.DONE&& xhr.status === 200)e.outerHTML = xhr.responseText;
};
});
});
});



请注意以下事项:




  • 此策略将要求您处理与原始引用元素相关联的任何特征,例如后退图像, id (或任何其他)属性,事件侦听器, iframe 功能等。

  • 确保只在图片文件实际加载后才尝试内联。

  • 考虑内联

  • 我在Firefox 44.0,Chrome 49.0和Opera 35.0中验证了这个策略(可以在服务器端允许提供单个文件,但内联在客户端允许图像文件缓存。 Mac和Windows),Safari 9.0(Mac)和IE 11(Windows)。

  • 此解决方案涉及从对象 iframe引用SVG文件 embed img 元素。它不会解决在CSS background-image 属性中或在<$ c> drawImage 函数中引用的外部SVG图像$ c>< canvas> 上下文。

  • 如果您试图通过复制和保存代码来复制本地计算机上的示例代码,然后打开 index.html ,Chrome和Opera有安全设置,这将使这不可能。要使这些浏览器在此类本地配置中工作,您需要按照此其他SO问题中的说明运行本地服务器>。如果您在正常的由服务器托管的网站中使用内联策略,则不应出现此类问题。


You can include SVG files into your HTML file via embed object and svg tags.

  • Using an embed or object tag requires linking the image with a URL. (This is what I prefer, because I don't like all that SVG code in my HTML code, so I’d like to keep it that way.)
  • Using the svg tag (AFAIK) requires having the SVG code inline in your HTML code.

My question is:

How do I include SVG icons, images, and other files into my HTML file, without having to put the entire SVG code in, and still be able to apply styles to them? Applying them through JS is fine too.

Note:

When I include them via object or embed, I can't seem to access them through jQuery, even with $("#my-svg-div").find("svg") (which, by the way, almost every answer on SO says I should). I just get undefined.

Thank you!

解决方案

UPDATE: Added an alternative strategy that also allows processing of SVG images in <img> elements, something not possible with the original strategy alone.

Short Answer

You can programmatically inline an SVG image. Such an image can be treated essentially identically to an actual inline <svg> element, including being able to have styles applied to it.

If your SVG image is referenced in an <object> or <iframe> element (e), you can inline it as follows:

e.parentElement.replaceChild(e.contentDocument.documentElement.cloneNode(true), e);

If your SVG image is referenced in an <embed> element, replace .contentDocument in the above code with .getSVGDocument().

If your SVG image is referenced in an <img> element, a completely different strategy, involving AJAX and described below, can be used to inline the image.

General Strategy

If your external SVG image files are same-origin (e.g. the images are loaded from the same place as the HTML code), then one approach that allows styling of these images is to programmatically inline them as follows:

  • Retrieve the content of the external SVG file.
  • Add that content directly back to your HTML file in the same HTML DOM location as the original referencing element, i.e. put it "in-line".
  • Delete the element that originally referenced the external SVG file.

Benefits

This inlining strategy gives you the best of two worlds:

  1. You get the benefits of separate image files, including:

    • organizing your image files independently of the HTML,
    • keeping your original HTML file uncluttered from image details, and
    • (potentially) allowing the browser to cache the images (but see below regarding this last point).
  2. Yet you can still do anything to the eventually-inlined SVG images that you could do to <svg> elements that were truly originally inline, including:

    • applying CSS stylings to them,
    • applying event listeners to individual SVG shapes or groups, etc.

Implementation

For <object> or <iframe> elements:

You can inline externally referenced SVG code as follows:

// using vanilla JavaScript (as shown above):
e.parentElement.replaceChild(e.contentDocument.documentElement.cloneNode(true), e);

// using jQuery:
$e.replaceWith($($e[0].contentDocument.documentElement).clone());

...where e or $e are the vanilla or jQuery variables (respectively) in which you have selected an external-SVG-referencing <object> or <iframe> element.

To include <embed> elements:

If, instead, you are using an external-SVG-referencing <embed> element, then you can inline the SVG content by replacing .contentDocument in the above code with .getSVGDocument() (note the additional parentheses). Note that .contentDocument does not work with <embed> elements while .getSVGDocument() actually works with all three element types. However .getSVGDocument() is deprecated and so should only be used if you really need <embed> elements.

To include <img> elements:

Neither of the above strategies works for <img> elements. To inline these, you can retrieve the src attribute of the <img> element, make an AJAX request for that file, create a new <svg> element using the retrieved SVG code and replace the original <img> element with the new <svg> element. If you want this strategy to work for all four element types, then just be aware that the URL of the referenced SVG image is held in the src attribute of <iframe>, <embed> and <img> elements but in the data attribute of an <object> element. This strategy can be implemented as follows:

// using vanilla JavaScript:
var xhr = new XMLHttpRequest();
xhr.open("GET", e.getAttribute(e.nodeName === "OBJECT" ? "data" : "src");
xhr.send();
xhr.onreadystatechange = function() {
  if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
    e.outerHTML = xhr.responseText;
  }
};

// using jQuery:
$.get($e.attr($e.prop("nodeName") === "OBJECT" ? "data" : "src"), function(data) {
  $e.replaceWith(data.documentElement);
});

Example

The following example demonstrates where the above strategy does and does not allow external CSS stylings to be applied to originally-external SVG images. (I didn't create a code snippet or jsfiddle because of the need to reference local external files.)

The following two screenshots show the CSS styling (red triangles) or lack thereof (black triangles) before and after inlining. It shows the results for SVG images originally embedded in the HTML (<svg>) or referenced in the indicated elements (<object>, <iframe>, <embed>, and <img>). The three rows show the results of inlining using the three indicated strategies.

Before clicking the button, no inlining has yet been attempted, and the screen looks like this. Only embedded SVG elements (the 1st column) are styled:

After clicking the button, inlining is attempted, and the screen now looks like this. CSS styling has been successfully applied to some of the elements:

The code required for this example follows:

image.svg (externally referenced file, i.e. not embedded in the HTML):

<svg xmlns="http://www.w3.org/2000/svg" width="50px" height="50px">
  <polygon points="25,5 45,45 5,45 25,5"/>
</svg>

index.html (obviously, remove the jQuery script line if not using jQuery):

<!DOCTYPE html>
  <head>
    <link href="styles.css" rel="stylesheet">
    <script src="//code.jquery.com/jquery-1.12.0.min.js"></script>
    <script src="main.js"></script>
  </head>
<body>
  <button>Click to attempt to inline svg images.</button>
  <table>
    <tr>
      <th></th>
      <th>svg   </th>
      <th>object</th>
      <th>iframe</th>
      <th>embed </th>
      <th>img   </th>
    </tr>
    <tr>
      <td>contentDocument</td>
      <td><svg  xmlns="http://www.w3.org/2000/svg" width="50" height="50"><polygon points="25,5 45,45 5,45 25,5"/></svg></td>
      <td><object data="image.svg" type="image/svg+xml"></object></td>
      <td><iframe  src="image.svg" width="50" height="50" style="border: none;"></iframe></td>
      <td><embed   src="image.svg" type="image/svg+xml" /></td>
      <td><img     src="image.svg" /></td>
    </tr>
    <tr>
      <td>getSVGDocument()<br />(deprecated)</td>
      <td><svg  xmlns="http://www.w3.org/2000/svg" width="50" height="50"><polygon points="25,5 45,45 5,45 25,5"/></svg></td>
      <td><object data="image.svg" type="image/svg+xml"></object></td>
      <td><iframe  src="image.svg" width="50" height="50" style="border: none;"></iframe></td>
      <td><embed   src="image.svg" type="image/svg+xml" /></td>
      <td><img     src="image.svg" /></td>
    </tr>
    <tr>
      <td>XMLHttpRequest</td>
      <td><svg  xmlns="http://www.w3.org/2000/svg" width="50" height="50"><polygon points="25,5 45,45 5,45 25,5"/></svg></td>
      <td><object data="image.svg" type="image/svg+xml"></object></td>
      <td><iframe  src="image.svg" width="50" height="50" style="border: none;"></iframe></td>
      <td><embed   src="image.svg" type="image/svg+xml" /></td>
      <td><img     src="image.svg" /></td>
    </tr>
  </table>
</body>
</html>

styles.css (only the polygon line is important for demonstrating the inlining):

polygon {fill: red;}
table {border-collapse: collapse;}
td, th {border: solid black 1px; padding: 0.4em;}

main.js (jQuery version):

$(document).ready(function() {
  $("button").click(function() {
    ["object", "iframe", "embed", "img"].forEach(function(elmtType) {
      var $e, $threeElmts = $(elmtType);
      $e = $($threeElmts[0]);
      if ($e[0].contentDocument) $e.replaceWith($($e[0].contentDocument.documentElement).clone());
      $e = $($threeElmts[1]);
      if ($e[0].getSVGDocument) $e.replaceWith($($e[0].getSVGDocument().documentElement).clone());
      $e = $($threeElmts[2]);
      $.get($e.attr($e.prop("nodeName") === "OBJECT" ? "data" : "src"), function(data) {
        $e.replaceWith(data.documentElement);
      });
    });
  });
});

main.js (vanilla JavaScript version):

document.addEventListener("DOMContentLoaded", function() {
  document.querySelector("button").addEventListener("click", function() {
    ["object", "iframe", "embed", "img"].forEach(function(elmtType) {
      var e, threeElmts = document.querySelectorAll(elmtType);
      e = threeElmts[0];
      if (e.contentDocument) e.parentElement.replaceChild(e.contentDocument.documentElement.cloneNode(true), e);
      e = threeElmts[1];
      if (e.getSVGDocument) e.parentElement.replaceChild(e.getSVGDocument().documentElement.cloneNode(true), e);
      e = threeElmts[2];
      var xhr = new XMLHttpRequest();
      xhr.open("GET", e.getAttribute(e.nodeName === "OBJECT" ? "data" : "src"));
      xhr.send();
      xhr.onreadystatechange = function() {
        if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) e.outerHTML = xhr.responseText;
      };
    });
  });
});

Note the following:

  • This strategy will require you to deal with any characteristics associated with the original referencing elements, e.g. fallback images, id and class (or any other) attributes, event listeners, iframe functionality, etc.
  • Ensure that you only attempt inlining after the image file has actually been loaded.
  • Consider inlining on the server-side to allow serving of a single file, but inlining on the client-side to allow image file caching.
  • I verified this strategy in Firefox 44.0, Chrome 49.0 and Opera 35.0 (Mac and Windows), Safari 9.0 (Mac) and IE 11 (Windows). I did not check Edge or any mobile browsers.
  • This solution addresses referencing SVG files from object, iframe, embed, and img elements. It does not address external SVG images referenced in CSS background-image properties or in the drawImage function of a <canvas> context. I suspect those won't work this way, but I haven't checked.
  • If you are attempting to duplicate the example code on your local computer by copying and saving the code and then "open"ing index.html, Chrome and Opera have security settings that will make this impossible. To get those browsers to work in such a local configuration, you instead need to run a local server as instructed in this other SO question. There should be no such problem if you are using the inlining strategy in a normal server-hosted web site.

这篇关于包括带有HTML的SVG文件,并且仍然能够向它们应用样式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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