SVG 有一个 <use>
元素,其本质含义是:去查找具有此 #identifier 的 SVG 片段,对其进行克隆,并将其放置在当前位置。它是 SVG 图标系统 的重要组成部分。关于这一点,我们有一些之前 未涉及 的内容需要了解。
作为提醒,它看起来像这样
<!-- Reference IN THIS SAME DOCUMENT -->
<svg>
<use xlink:href="#icon-1"></use>
</svg>
<!-- EXTERNAL reference -->
<svg>
<use xlink:href="sprite.svg#icon-1"></use>
</svg>
该 #icon-1 标识符很可能引用该文件中的一个符号,例如…
<svg xmlns="http://www.w3.org/2000/svg">
<symbol id="icon-1" viewBox="0 0 1024 1024">
<title>Kinda like alt text</title>
<path class="path-1" d="..."></path>
</symbol>
...
</svg>
外部引用的好处在于它可以很好地利用浏览器缓存。
此外,它也很容易使用。您只需将正确的文件路径提供给文件(“SVG sprite”)并引用一个标识符,它就能正常工作。您可以让服务器设置该文件的所有正确标头,以便浏览器像保留任何其他要缓存的资产一样保留它。
问题在于 IE,但它正在被修复。
大多数支持内联 SVG 的浏览器都支持此功能,因此它几乎可用,但主要例外是 IE。但是他们已经 在 Microsoft Edge 中修复了该问题。Edge 还没有很大的市场份额,但它是 Windows 的未来,因此最终我们将能够在没有任何额外工作的情况下开始使用它。
由于缺乏完美支持,因此有两种主要的解决方法。
- 在所有 HTML 文档中包含 SVG sprite。效果很好。通常非常快,但会增加页面缓存的大小。为了获得最佳支持,您需要将其包含在文档顶部,这意味着可能更重要的内容的渲染会稍有延迟。或者…
- 使用 Ajax 加载 sprite。 然后可以利用浏览器缓存。但是,如果没有一点 FONI(无图标闪烁),这可能很难做到。
一旦我们能够直接开始使用外部引用,情况就不一样了
这个概念让我感到困惑,这就是我想要写这篇文章的原因。
我以为外部引用是最终的解决方案,因为它可以完成内联 SVG 引用同一文档中 SVG 所能完成的所有操作。但不幸的是,它做不到。以这种方式引用的 SVG 具有其自己的独立 DOM。它超出了所有 <use>
都受制于的常规 Shadow DOM 边界。
使用此方法
<svg class="icon-1">
<use xlink:href="#icon-1"></use>
</svg>
您可以编写 CSS(在您用于网站其余部分的同一样式表中)来对其进行着色
/* This works.
It will cascade this fill through the shapes,
as long as there are no presentational fill
attributes on the shapes themselves. */
.icon-1 {
fill: red;
}
实际上,您仍然可以使用外部引用的 <use>
来做到这一点。但是您无法像以前那样设置单个形状的样式。
/* You could reach individual shapes
to style because they share the same DOM.
But this WON'T WORK with externally referenced SVG. */
.path-1 {
fill: yellow;
}
/* This won't work either way,
because it crosses a shadow DOM boundary */
.icon-1 /* ~shadow~ */ .path-1 {
fill: yellow;
}
当您外部引用时,您根本无法访问内部形状。例如,从 HTML 文档中
<script>
var shape = document.querySelectorAll(".path-1");
console.log(shape);
// [ ] (empty set)
</script>
这是一个 gist,它有点强调了这一点。
它仍然很酷。
级联单一颜色仍然有效的事实使其非常有用。大多数图标往往是单色的。而且您仍然可以拥有不同的单色。
奇怪的未来事物
Tab Atkins 以其疯狂的未来思维方式记录了一些潜在的未来想法,称为 SVG 参数
.foo {
background-image: url("http://example.com/image.svg" param(--color var(--primary-color)));
}
这是一个 CSS 示例,但大概内联 SVG 也能使用。
查询字符串缺少点 (
class
) :p谢谢!已修复,并将其隐藏,因为不再是问题。
嘿,Chris,好东西。
只是提醒一下您的读者:当使用
<use>
时,您仍然可以通过使用 fill 和 color css 属性以及在 SVG 中设置 current-color 值来为 SVG 着色。我在这里比你做得更好 https://css-tricks.cn/cascading-svg-fill-color/我现在正在使用此技术在一个项目上工作,它非常适合我的需求(我的所有图标最多使用 3 种颜色,因此我通过 SVG 中的透明区域在 SVG 的包装器上使用 fill、color 和 background-color)。
请注意,外部主机(例如当您想从 CDN 提供 sprite 时)不一定有效,因为大多数浏览器中都没有为此类请求实现 CORS:http://tympanus.net/codrops/2015/07/16/styling-svg-use-content-css/comment-page-1/#comment-466665
是的,这非常重要!
您可能会认为 CORS 标头允许这样做,但事实并非如此。我希望这被认为是一个将要修复的错误,但我不确定(也许它在某种程度上很危险,因此永远不会被允许?)。
因此,您可以将其引用为外部 URL,但只能在同一域名下。
好吧,这是 Chromium 上的一个已报告的错误(不确定其他浏览器),但还没有太多活动:https://code.google.com/p/chromium/issues/detail?id=470601。仍然希望它能尽快得到修复。
当正确配置 CORS 和 CSP 时,我看不到允许它的任何风险。
我找到了一种使用
use
和symbol
创建外部 sprite 表的方法,并且能够像内联一样使用普通的 CSS 为符号内的每个元素设置样式。最棒的是,它可以在所有浏览器中运行,而且您可能已经在使用正确的库。svg4everybody 所做的是用实际符号的内容替换
use
标记,但仅在 IE 和旧版 Safari 中。通过设置polyfill:true
,您可以强制所有浏览器都使用此行为。我在 https://github.com/jonathantneal/svg4everybody/issues/82 上建议了它,并且在 http://toyotahalloffame.com 上成功使用了它。
因此,据推测,当我们想要停止使用 svgforeverybody 因为不再需要支持旧版 IE 时,有人可以将此功能提取到一个更小的 polyfill 中,我们都可以使用它?我可能无法扫描 svg4everybody 代码并找出其中的奥秘,但我相信那里有一些聪明的人可以制作一个小的 polyfill 来帮助我们解决这个问题?
更好的是:然后每个实例都完全独立,因此您甚至可以以不同的方式设置多个形状内部的样式。这只是以 1) 一个额外的(小)库 2) 至少一个 XHR 请求(它是否对每个图标执行一次或总共执行一次?) 3) dom 权重为代价的。
Jon:我认为没有需要提取的东西;svg4everybody 恰好做了这件事,但默认情况下它只将其作为“polyfill”执行。唯一可以改进的地方是删除 UA 检查。
这不是原因。原因是 CSS 规则不适用于文件边界。您无法对其进行样式设置,因为它位于单独的文件中。
这正是我想表达的意思。这是一个不同的 DOM。但是您仍然可以看到该 DOM,例如,如果您在 Web 中检查它。因此,它看起来就像一个普通的
<use>
,带有 Shadow DOM 及其所有内容,但它遵循不同的规则。不,浏览器获取外部文件,并在文档内复制已使用的
<symbol>
作为 Shadow DOM。因此,这在您的声明的 HTML中…在文档中转换为以下内容
…并且您无法通过 Shadow Root 进行选择。
好吧,有时您可以
在 Firefox 中,
<use>
的实现有点旧,早于 Shadow DOM 规范,并且允许您选择“复制”的元素。这本身就是一个错误,它也可能在您自己的代码中创建错误,因为如果您执行svg { fill: red; }
,它也会应用于内部 SVG 元素,并且当您稍后尝试使用例如.cool-icon { fill: blue; }
更改特定图标的颜色时,该图标将保持红色。在 Chrome 中,您可以使用穿透阴影组合器,如下所示:
svg /deep/ path { fill: green; }
,但此实验性功能已弃用,并且预计将来会被删除(根据最新的 Web Components 面对面会议的决定)。Chris 的观点很棒,
虽然我喜欢 SVG(精灵图),但需要考虑很多问题。
另一件事:由于人们复制粘贴我们的代码示例,因此如果我们能够提倡最健壮和最易访问的标记,那将非常棒。
我不是无障碍专家,但据我了解,我们可以/应该采取一些额外的步骤来使我们的 SVG 精灵图标更易访问。
以下是我目前的思路,我希望得到无障碍专家的建议!
首先:区分“表现性”和“内容”图标。就像我们对
img
及其alt
属性所做的那样。对于所有 SVG 图标精灵图(在
sprite.svg
中)将
title
元素子元素添加到精灵图的symbol
中将
desc
元素子元素添加到精灵图的symbol
中现在,对于将这些符号用作纯表现性图标的
use
在 SVG 上使用
role="presentation"
就是这样。但是对于作为“内容”的 SVG 图标,我们应该做更多的事情
添加
role="img"
添加
title
属性添加带有唯一
id
的title
元素添加
aria-labelledby="title-id"
据我所知,这可以确保最佳的无障碍性。
再说一次:我不是无障碍专家,因此请在我不正确时纠正我。
源信息
http://codepen.io/NathanPJF/full/GJObGm
https://www.paciellogroup.com/blog/2013/12/using-aria-enhance-svg-accessibility/
https://github.com/jonathantneal/svg4everybody#readability-and-accessibility
有趣的是,我下载了你的 gist,在预览框中看不到任何内容。
Chrome:显示绿色黑桃,黑色铅笔
Firefox:显示绿色黑桃,绿色铅笔
IE:仅显示绿色黑桃
您是否使用真实的 Web 服务器运行它?这可能是必要的。
我可以让它工作吗?=)
抱歉,演示=)
http://nezed.github.io/SVG-External-Reference-as-bg/
几周前我处理过这个问题。我建议我的一个公司客户进行图标重构,该重构将替换旧的 png 精灵图,因为我们需要大量的自定义。
从 Chris 关于此的第一篇文章开始,外部引用就一直在我脑海中,但当我意识到系统中有一些无法使用此技术单独设置样式的多部分图标时,我感到恐慌。
幸运的是,我找到了Osvaldas Valutis的一篇文章,该文章使用 localStorage 缓存精灵图并将其内联注入。如果 localStorage 已满,它会加载并注入精灵图。这就是我处理它的方式,尽管此技术并不完美,并且会使图标的渲染速度降低一小部分时间。
我一定会尝试Federico Brigante使用 svg4everybody 的小技巧的技术,看看哪一个性能更好!
继续加油,Chris!
一些额外的要点
正如 Anselm Hannemann 在上面提到的,目前尚无方法在 SVG 中启用 CORS 内容。这是规范中计划添加的功能,但在浏览器中尚不支持。
尽管 SVG 1.1 规范建议在外部文件中使用 CSS 设置的样式仍然应该应用于克隆的内容,但实际上这非常成问题。SVG 2 将其留给浏览器是否处理外部文档中的任何样式表块,因此警告作者不要依赖外部文件中的任何
<style>
规则。表示属性和内联style
属性应按正常方式工作。在确定外部文件中元素的百分比值方面也存在一些问题,因为该文件没有视口来创建参考 100% 宽度/高度。但是,在使用
<symbol>
时,这永远不会成为问题,因为符号会为百分比创建自己的参考框架。无论来自相同文件还是外部文件,重复的内容都将可以通过继承的 CSS 自定义属性(也称为 CSS 变量)进行样式设置。查看已在 Firefox 中运行的演示的博文。您将在 URL 函数中指定的 SVG 参数将映射到根元素继承的 CSS 自定义属性。对于
<use>
元素,您只需使用 CSS 在<use>
上设置它们,重复的内容将继承它们。如果其中一个符号链接到同一文件中的渐变,并且此文件用作外部引用,则渐变将不会显示在 Chrome 中,任何其他引用(例如过滤器)也是如此
在我的项目中遇到了这个问题:https://github.com/w0rm/gulp-svgstore/issues/56