包含带有 HTML 的 SVG 文件,并且仍然能够对它们应用样式?

Posted

技术标签:

【中文标题】包含带有 HTML 的 SVG 文件,并且仍然能够对它们应用样式?【英文标题】:Include SVG files with HTML, and still be able to apply styles to them? 【发布时间】:2016-06-17 10:39:17 【问题描述】:

您可以通过 embed objectsvg 标签将 SVG 文件包含到您的 html 文件中。

使用embedobject 标记需要将图像与URL 链接。 (这是我更喜欢的,因为我不喜欢 HTML 代码中的所有 SVG 代码,所以我想保持这种方式。) 使用 svg 标签 (AFAIK) 需要在 HTML 代码中内嵌 SVG 代码。

我的问题是:

如何在我的 HTML 文件中包含 SVG 图标、图像和其他文件,而无需将整个 SVG 代码放入其中,并且仍然能够对其应用样式?通过 JS 应用它们也很好。

注意:

当我通过 objectembed 包含它们时,我似乎无法通过 jQuery 访问它们,即使使用 $("#my-svg-div").find("svg")(顺便说一下,几乎所有关于 SO 的答案都说我应该这样做)。我刚收到undefined

谢谢!

【问题讨论】:

基本上......你不能......很容易就是 - ***.com/questions/4906148/… 好的.. 所以我假设能够访问 SVG 的 dashArrayOffset,意味着我也可以访问样式。我有一个对象标签,这个插件能够绘制 SVG 的笔触,即使它是一个对象而不是 SVG。 github.com/maxwellito/vivus我现在在看代码……看不到任何有用的东西 好吧,dashArrayOffset 是一个 CSS 属性,所以,除非它嵌入到 SVG 本身中,否则是的。 ...但您必须对其进行测试。 【参考方案1】:

这是关于 (1) 在 HTML 中使用 SVG 的不同方式和 (2) 您可以通过 CSS/JS 对 SVG 的各个部分(即:路径)进行样式设置的最全面的演练。

https://css-tricks.com/using-svg/

    在图片标签中(即:<img src="picture.svg" /> 或 CSS 中的背景图片 = 无样式

    内联 - 样式消失,但它会使您的 HTML 混乱。 php 在这里有帮助,或者您可以使用 gulp 构建任务或其他东西来防止 SVG 使您的工作代码变得混乱,同时最终仍然是内联的。

    作为一个对象,您现在可以在 .svg 文件中添加 CSS:

    <svg ...>
      <style>
        /* SVG specific fancy CSS styling here */
      </style>
      ...
    </svg>
    

    <?xml-stylesheet type="text/css" href="svg.css" ?>
    

    数据 URI - 非常适合背景图像。没有样式。

【讨论】:

您还可以使用 javascript 将动态 CSS 注入带有 object 标签的 SVG。 真的吗?你能不能一起扔一个codepen来演示。我真的很想玩这个。【参考方案2】:

简答

您可以以编程方式内联 SVG 图像。这样的图像可以被视为与实际的内联 &lt;svg&gt; 元素基本相同,包括能够对其应用样式。

如果您的 SVG 图像在 &lt;object&gt;&lt;iframe&gt; 元素 (e) 中被引用,您可以按如下方式内联它:

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

如果您的 SVG 图像在 &lt;embed&gt; 元素中被引用,请将上述代码中的 .contentDocument 替换为 .getSVGDocument()

如果您的 SVG 图像在 &lt;img&gt; 元素中被引用,则可以使用一种完全不同的策略(涉及 AJAX,如下所述)来内联图像。

总体战略

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

检索外部 SVG 文件的内容。 将该内容直接添加回您的 HTML 文件,位于与原始引用元素相同的 HTML DOM 位置,即“内嵌”。 删除最初引用外部 SVG 文件的元素。

好处

这种内联策略为您提供了两全其美:

    您可以获得单独的图像文件的好处,包括:

    独立于 HTML 组织图像文件, 让您的原始 HTML 文件远离图像细节,并且 (可能)允许浏览器缓存图像(但关于最后一点,请参见下文)。

    但是,您仍然可以对最终内联的 SVG 图像执行任何操作,就像对真正最初内联的 &lt;svg&gt; 元素执行的操作一样,包括:

    对它们应用 CSS 样式, 将事件侦听器应用于单个 SVG 形状或组等。

实施

对于&lt;object&gt;&lt;iframe&gt; 元素:

您可以按如下方式内联外部引用的 SVG 代码:

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

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

...其中e$e 是vanilla 或jQuery 变量(分别),您在其中选择了外部SVG 引用&lt;object&gt;&lt;iframe&gt; 元素。

要包含&lt;embed&gt; 元素:

如果您使用的是外部 SVG 引用 &lt;embed&gt; 元素,则可以通过将上述代码中的 .contentDocument 替换为 .getSVGDocument() 来内联 SVG 内容(请注意额外的括号)。请注意,.contentDocument 不适用于 &lt;embed&gt; 元素,而 .getSVGDocument() 实际上适用于所有三种元素类型。但是 .getSVGDocument() is deprecated 等只有在您确实需要 &lt;embed&gt; 元素时才应使用。

要包含&lt;img&gt; 元素:

上述策略都不适用于&lt;img&gt; 元素。要内联这些,您可以检索 &lt;img&gt; 元素的 src 属性,对该文件发出 AJAX 请求,使用检索到的 SVG 代码创建一个新的 &lt;svg&gt; 元素,然后将原始 &lt;img&gt; 元素替换为新的&lt;svg&gt; 元素。如果您希望此策略适用于所有四种元素类型,请注意,引用的 SVG 图像的 URL 保存在 &lt;iframe&gt;&lt;embed&gt;&lt;img&gt; 元素的 src 属性中,但在 @ &lt;object&gt; 元素的 987654364@ 属性。该策略可按如下方式实现:

// 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);
);

示例

以下示例演示了上述策略允许和不允许将外部 CSS 样式应用于原始外部 SVG 图像的位置。 (由于需要引用本地外部文件,我没有创建代码 sn-p 或 jsfiddle。)

以下两个屏幕截图显示了内联前后的 CSS 样式(红色三角形)或缺少样式(黑色三角形)。它显示了最初嵌入 HTML (&lt;svg&gt;) 或在指定元素中引用的 SVG 图像的结果(&lt;object&gt;&lt;iframe&gt;&lt;embed&gt;&lt;img&gt;)。三行显示了使用指定的三种策略进行内联的结果。

在单击按钮之前,尚未尝试内联,屏幕如下所示。只有嵌入的 SVG 元素(第一列)有样式:

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

此示例所需的代码如下:

image.svg(外部引用的文件,即未嵌入 HTML):

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

index.html(显然,如果不使用 jQuery,请删除 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"  ><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"   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"  ><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"   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"  ><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"   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(只有 polygon 行对于演示内联很重要):

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

main.js(jQuery 版本):

$(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(原生 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(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;
      ;
    );
  );
);

注意以下几点:

此策略将要求您处理与原始引用元素相关的任何特征,例如备用图像、idclass(或任何其他)属性、事件侦听器、iframe 功能等。 确保仅在实际加载图像文件后尝试内联。 考虑在服务器端内联以允许提供单个文件,但在客户端内联以允许图像文件缓存。 我在 Firefox 44.0、Chrome 49.0 和 Opera 35.0(Mac 和 Windows)、Safari 9.0(Mac)和 IE 11(Windows)中验证了这个策略。我没有检查 Edge 或任何移动浏览器。 此解决方案解决了从 objectiframeembedimg 元素引用 SVG 文件的问题。它不处理 CSS background-image 属性或 &lt;canvas&gt; 上下文的 drawImage 函数中引用的外部 SVG 图像。我怀疑这些不会以这种方式工作,但我没有检查过。 如果您试图通过复制并保存代码然后“打开”index.html 在本地计算机上复制示例代码,Chrome 和 Opera 的安全设置将使此操作成为不可能。要让这些浏览器在这样的本地配置中工作,您需要按照this other SO question 中的说明运行本地服务器。如果您在普通的服务器托管网站中使用内联策略,应该不会出现这样的问题。

【讨论】:

非常有趣,我可能会在接下来的几天里添加我自己发现的 Codepen。 天哪。你拥有这个答案。我希望它得到应有的重视。干得好。 感谢@tayvano 的夸奖。我(希望)通过包含替代策略(使用 AJAX)来允许内联 这是外部 SVG 资源的理想答案。异步加载 SVG 内容然后将它们作为 DOM 中的 SVG 代码注入更为实用。

以上是关于包含带有 HTML 的 SVG 文件,并且仍然能够对它们应用样式?的主要内容,如果未能解决你的问题,请参考以下文章

SVG:简化去除曲线的路径?

将带有掩码和符号的 SVG 转换为纯 SVG 文件?

带有svg图像模板的angularjs

如何将带有css样式的内联SVG从浏览器保存/导出到图像文件

将图像放在另一个 png/svg 文件中

如何用其他数据替换 gzip 压缩数据并且仍然有有效的 .gz 文件?