SVG 回退指南

Avatar of Amelia Bellamy-Royds
Amelia Bellamy-Royds

如果您正在使用 SVG 并担心不支持的环境,本指南适合您。 由于您使用 SVG 的方式决定了回退方式,因此没有单一的解决方案。

由 DigitalOcean 提供

DigitalOcean 提供您在任何阶段都需要的云计算服务来支持您的发展。 立即开始使用 200 美元的免费额度!

在 CSS-Tricks 上,有很多信息告诉您 如何 奇妙的 SVG 。 尽管我们希望说服您 SVG 适用于每个人,但 SVG 的使用并没有像我们希望的那样广泛。 事实上,有些人仍然(确实)无法理解 SVG。

也许他们被困在自 IT 人员离职六年后就没有更新过的办公室电脑上,也许他们使用的是翻新的二手手机,无法负担升级,或者也许他们只是不理解或不在乎更新到新浏览器。 无论原因是什么,大约有 5% 的用户使用无法显示 SVG 的 Web 浏览器浏览网页——在美国更是如此(根据 caniuse 数据)。

一些技术导向的网站(比如这个网站)可以负担得起只支持现代 Web 浏览器的说法。 但对于大多数网站来说,您不能无视 20 个潜在客户中的 1 个。 如果你想让 95% 使用现代浏览器的用户享受 SVG 的所有好处,同时仍然为其他人提供功能性体验,你需要一个回退计划。

本指南由 Amelia Bellamy-Royds 和我 Chris Coyier 撰写。 Amelia 和我在同一个会议上做主题演讲。 我们都涵盖了 SVG,但都没有全面介绍 SVG 回退。 毕竟,这是一个非常大的话题。 虽然我之前已经介绍过 SVG 回退,但已经过去几年了,我们认为现在可以更全面地介绍这个主题。 开始吧!

您需要哪种回退?

在开始研究实现回退的技术选项之前,先停下来想一想您需要哪种回退。

  • 无回退。 如果 SVG 是一个图标,其含义可以通过文本标签清楚地表达,您可以让该图标消失,而不会影响网站的功能。
  • 文本回退。 如果 SVG 是一个图标,其含义可以通过文本标签表达,您可能只需要确保替代文本显示在它的位置即可。
  • 图像回退。 这是大多数人认为的 SVG 回退:一个 PNG 或 GIF 图像,代表相同的图形,只是文件大小更大,分辨率更差。
  • 交互式回退。 对于替换动画和交互式 SVG,PNG 可能无法满足要求。 您需要使用具有交互式 DOM 的图形语言。

在交互式方面,您的选择有限。 您可以 将 SVG 转换为 Flash,然后在 ActionScript 中重新编写所有交互代码。 或者,您可以使用 Raphaël 来绘制和操作图形。 它为 SVG 和 Internet Explorer 6-8 支持的 VML 矢量图形提供了一个 JavaScript 接口。 这意味着 Raphaël 无法帮助解决旧版移动浏览器的问题,但它确实显著减少了不支持的用户数量。

本文的其余部分将介绍如何为您的 SVG 创建文本和图像回退。 您的选择几乎完全取决于您在网页中包含 SVG 的方式:作为嵌入对象、作为内联 SVG 代码、作为 HTML 中的图像,还是作为 CSS 中的图像。


作为 <img> 的 SVG 回退

SVG 可以像这样使用

<img src="image.svg" alt="">

以下是一些针对不支持该功能的浏览器的选项。

如何测试支持

即使该测试 应该测试 SVG 中的 <image> 元素,但由于 一些测试 我们已经证明,它适用于用作 <img> 元素源的 SVG。 它就是 如此简单

function svgasimg() {
  return document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image", "1.1");
}
源代码交换

上面的测试是 SVGeezy 库 用来执行 SVG-as-img 回退的。 如果浏览器未能通过此测试,它将根据需要将 SVG 与 PNG 交换。 如 <img src="image.svg"> 变为 <img src="image.png">。 您自己创建 PNG 版本,并将它们放在同一个目录中。

  • 优点:它很简单。 它有效。 您甚至可以非常轻松地编写自己的源代码交换 JavaScript。
  • 缺点:不支持的浏览器可能会下载两个图像,这会影响性能。 它会下载 SVG 版本(至少要下载足以知道它无法使用它),然后下载 PNG 版本。

这是一个非常简单的无依赖项的源代码交换示例

在 Internet Explorer 6(!)中交换回退图像

如果由于某种原因您无法将其放到外部 JavaScript 中,而必须使用内联方式……

<img src="image.svg" onerror="this.src='image.png'; this.onerror=null;">

如果您使用 Modernizr 进行特性检测,并且在项目中也使用 jQuery,它可以像这样简单

if (!Modernizr.svg) {
  $("img[src$='.svg']")
    .attr("src", fallback);
}

SVGInjector 是一个 JavaScript 库,可以帮助在特定情况下进行 <img> 回退。 它的主要目的是用内联 SVG 替换 <img>

<img class="inject-me" src="image-one.svg">
var mySVGsToInject = document.querySelectorAll('img.inject-me');
var injectorOptions = {
  pngFallback: 'assets/png'
};
SVGInjector(mySVGsToInject, injectorOptions);

SVGMagic 是另一个 JavaScript 库,它用 PNG 版本替换源,包括 <img>、background-image 或甚至内联使用的 SVG。 它最大的优势是它可以根据请求自动创建 PNG 版本,并将其发送到第三方服务器。 因此,请注意依赖项(jQuery 和第三方)并测试速度和可靠性。

<picture> 元素

<picture> 元素允许在浏览器不支持指定图像格式时提供回退图像。

<picture>
  <source type="image/svg+xml" srcset="image.svg">
  <img src="image.png" alt="">
</picture>

不幸的是,当对 SVG 的支持远高于对 <picture> 的支持时,这并没有什么用处。

您可以使用 Picturefill 对 picture 元素进行 polyfill。 Sara Soueidan 有一篇关于此的文章

  • 优点:您不仅可以获得回退图像,还可以指定不同的回退,作为 picture 语法的一部分,针对不同的媒体查询。 意味着针对不同的屏幕、艺术指导等提供不同大小的回退。
  • 缺点:需要 polyfill。 为了避免双重下载,您需要跳过 <picture> 中的 <img> 的 src,这是无效的,这意味着您将永远需要 polyfill。
<image> 技巧

通过一些内联 SVG 技巧,我们可以实现这一点

<svg width="96" height="96">
  <image xlink:href="image.svg" src="image.png" width="96" height="96" />
</svg>

请务必将大小属性添加到图像,使其填满整个 SVG,然后在 CSS 中设置 svg(针对现代浏览器)和 image(针对旧版浏览器)的大小。

除了排除一些可以支持 SVG 图像但不能支持内联 SVG 的旧版浏览器外,这里的另一个主要限制是,让图像在流式网站上显示正确的大小将需要更多工作。 您需要使用 与让 SVG 扩展到一致的纵横比相同的技巧,而不仅仅是设置 width: 100%; 并让高度自动调整。

  • 优点:不需要 JavaScript 或任何其他依赖项。
  • 缺点:在 IE 中触发多个(中止)请求。 旧版 iOS 即使支持 SVG,也可以显示回退。 测试

作为 <object> 的 SVG 回退

随着内联 SVG 的支持度越来越高,SVG-as-<object> 已经不再流行。 从渐进增强/优雅降级的角度来看,这很不幸,因为这是提供回退内容的最简单方法。

如果 <object> 本身无法显示,则会显示其子内容。 该内容可以是 任何 html 内容:图像、格式化文本,甚至另一个对象(例如,包含动画 SVG 的 Flash 版本)。

<object type="image/svg+xml" data="svg-ok.svg">
  <img src="svg-no.png" alt="No SVG support">
</object>
<object type="image/svg+xml" data="svg-ok.svg">
  <p class="warning">
    Your browser does not support SVG!
  </p>
</object>

这是一个简单的测试。

<object> 的另一个渐进增强优势:Internet Explorer 8(及以下)的一些用户将能够看到 SVG! 对象会触发插件,并且不再更新的 Adobe SVG Viewer 插件 仍然可以下载用于旧版 IE。

作为 CSS background-image 的 SVG 回退

对于大多数 CSS 属性,您可以信任 CSS 错误处理,让浏览器忽略新的语法,并应用级联中之前声明的值。 但是,使用 SVG 图像文件的 CSS 规则的语法对于旧版浏览器来说是完全正确的。 因此,它们会应用该规则,下载文件,但随后不知道如何处理它。

这里的诀窍是找到支持 (几乎所有) 支持 SVG 的浏览器,但不支持旧版浏览器的语法。 以下是神奇之处

body {
  background: url(fallback.png);
  background: url(background.svg),
    linear-gradient(transparent, transparent);
}

这将两个功能结合在一起,共同打造完美的组合。如果浏览器同时支持多个背景和线性渐变,它也支持 SVG。因此,我们在这里声明了一个完全透明的线性渐变的 SVG。如果在旧的浏览器中失败,上面声明的回退将生效。

您可能在现代浏览器上添加了轻微的性能损失,因为需要计算透明的渐变,但这可能微不足道。

Internet Explorer 7 中的替换背景图像
Android 2.2 中的替换背景图像

内联<svg>的回退

内联 SVG 由于多种原因而流行。用于绘制所需内容的所有代码要么直接位于标记中(减少请求),要么从可缓存的文件中引用。您对内联的 SVG 有很多控制权。它位于 DOM 中,因此可以使用来自同一文档的 CSS 和 JavaScript 控制它。

如何测试支持

有一些来自 Modernizr 的很棒的测试,我们可以看看。您在内联 SVG 中提取到函数的最佳选择是这个

function supportsSvg() {
  var div = document.createElement('div');
  div.innerHTML = '<svg/>';
  return (div.firstChild && div.firstChild.namespaceURI) == 'http://www.w3.org/2000/svg';
};

这测试了 HTML 解析器(用于解析发送到 innerHTML 的内容)是否可以正确生成 SVG 元素,基本上是通过创建元素,注入 SVG 并测试命名空间。

<object>一样,不支持<svg>的浏览器会忽略您的 SVG 标记,并将它的内容视为 HTML。但这并不简单。使用<object>,支持对象的浏览器将知道忽略子内容。对于内联 SVG,没有这样的内置回退。但是,您可以使用一些解决方法。

内联 SVG 中的纯文本回退

可以在内联 SVG 代码中包含纯文本,并且任何支持 SVG 的浏览器都会忽略它,因为 SVG 文本必须包含在<text>元素中。您甚至可以在该文本中包含超链接。

<svg viewBox="-20 -20 40 40">

  <!--Text Fallback-->
  I'm sorry, your browser does not support

  <circle fill="limegreen" r="19" />
  <path stroke="forestgreen" fill="none" stroke-width="6"
        d="M-12,3 L-3,10 11,-12" />
  <text dy="0.35em" text-anchor="middle" font-weight="bold"
        font-size="18px" font-family="sans-serif"
        fill="indigo">SVG</text>

  <!--Fallback with links-->
  Please upgrade to a <a href="http://browsehappy.com/?locale=en">modern browser</a>.

</svg>
纯文本回退,它显示在 Android 2.3 中

注意事项

  • SVG 文本(本演示中的“SVG”一词)成为回退文本的一部分。我们稍后将讨论如何避免这种情况。
  • SVG<title>文本不会成为回退文本的一部分。这是因为不支持 SVG 的浏览器将此解释为无效的第二个 HTML 标题元素,并忽略它。

您不能在内联 SVG 中包含任何其他 HTML 内容:当现代浏览器中的 HTML 解析器遇到 HTML 标记时,它会假设您忘记关闭<svg>标记,并为您关闭它。这意味着 (a) 您的 SVG 已损坏,并且 (b) 您的回退文本在现代浏览器中可见。您可以使用链接 (<a>标记) 的唯一原因是,它们在 SVG 中是有效的标记,但不会自行绘制任何内容。


另一种方法是从 HTML 文本开始,并在使用 JavaScript 检测到它支持的情况下将其替换为内联 SVG。想象一下 HTML 中的“喜欢”按钮

<button aria-label="Like">
  <span class="inline-svg" data-xlink="#icon-heart">♥</span> Like
</button>

带有 ♥ 的跨度是那里的回退。数据属性是我们将在回退中使用的属性,在本例中是<svg>/<use>元素。

if (supportsSvg()) { // see test above
  var inlineSvgs = document.querySelectorAll('span.inline-svg');

  for(i = 0; i < inlineSvgs.length; i++) {
    var span = inlineSvgs[i];
    var svgns = "http://www.w3.org/2000/svg";
    var xlinkns = "http://www.w3.org/1999/xlink";
    var svg = document.createElementNS(svgns, "svg");
    var use = document.createElementNS(svgns, "use");

    // Prepare the <use> element
    use.setAttributeNS(xlinkns, 'xlink:href', span.getAttribute('data-xlink') );

    // Append it
    svg.appendChild(use);

    // Prepare the SVG
    svg.setAttribute('class', "inline-svg");

    // Set a title if necessary.
    if (span.getAttribute('title')) {
      svg.setAttribute('title', span.getAttribute('title'));
    }

    // Inject the SVG
    span.parentNode.insertBefore(svg, span);

    // Remove fallback
    span.remove();
  }
}
内联 SVG 中的 HTML 格式化文本回退

可以使用 HTML 文本格式标记,而不会破坏 SVG,方法是在 SVG<desc>(描述)标记内包含它,这些标记允许来自其他命名空间的内容。

<svg viewBox="-20 -20 40 40">

  <desc>
    <p class="warning">
        Fallback text.
    </p>
  </desc>

  <!-- ... SVG content ... -->

</svg>
格式化文本回退,它显示在 Internet Explorer 8 中

注意事项

  • <desc>标记的主要目的是提供替代文本描述。确保标记的内容对屏幕阅读器有意义。
  • 为了让<desc>在所有浏览器上被辅助技术识别,它应该位于 SVG 的顶部,紧随<title>之后。
  • 应该还可以使用 SVG<metadata>标记来包含所有类型的任意标记,而不会影响 SVG 或它的替代文本。但是,这在测试过的浏览器中不起作用(它们插入隐式</svg>结束标记)。
  • SVG 文本内容仍然包含在回退文本中。

可以甚至在<desc>中包含带有回退图像的<img>标记,它将在旧浏览器中正确显示,而不会破坏新浏览器中的 SVG。不要这样做!虽然现代浏览器不会显示回退图像,但它们确实会下载它,我们总是试图避免双重下载。

内联 SVG 的background-image回退

内联<svg>回退的一种可能性是设置一个仅在浏览器不支持内联<svg>时使用的背景图像。因此,使用上面的测试,您可以给自己一个可以使用的类

if (!supportsSvg()) {
  document.documentElemement.classList.add("no-svg");
  <em>// or even .className += " no-svg"; for deeper support</em>
}

然后假设您正在使用一些像这样的内联 SVG

<button>
  <svg class="icon icon-key">
    <use xlink:href="#icon-key"></use>
  </svg>
  Sign In
</button>

您可以使用该新类在需要时应用背景图像

html.no-svg .icon-key {
  background: url(fallback-key.png) no-repeat;
}

演示

您将必须自己创建 PNG 并调整大小。这里有一篇关于使用 Grunticon 自动执行所有这些操作的CSS-Tricks 的介绍文章

为了获得最深层的支持,您将<svg>包装在<div>中,并将背景应用于它,因此即使浏览器完全拒绝 SVG 元素,回退仍然可以在已知的<div>元素上工作。

内联 SVG 中的<img>回退

我们在上面的“<image>技巧”部分中介绍了内联回退,但由于这实际上是使用内联 SVG,所以让我们在这里详细介绍。要在 SVG 中获得图像回退,无需额外下载,您需要一种包含图像的方法

  • 旧浏览器会将其识别为有效的 HTML
  • HTML 5 解析器将在<svg>内允许
  • 现代浏览器不会将其解释为要下载的图像。

可能看起来很奇怪,但 SVG<image>元素实现了这个目的。SVG<image>元素用于在 SVG嵌入其他图像文件。但是,在 HTML 中,每个测试过的浏览器都将<image>识别为<img>的非标准同义词。在 SVG 中,您使用xlink:href属性指定图像文件的 URL。在 HTML 中,您使用src属性指定它。

因此,在大多数浏览器中,在内联 SVG 中包含带有src属性(指向您的回退图像)的<image>标记就足够了:旧浏览器将下载回退,新浏览器不会。除了Internet Explorer,它即使不显示回退图像也会下载它。解决方法是在元素上放置一个空xlink:href属性。IE 开发者工具仍然显示它请求回退,但它几乎立即中止(在 IE11 或 IE10/IE9 模拟模式下 < 1ms),在下载任何内容之前。它看起来像这样

<svg viewBox="-20 -20 40 40">
  <!-- SVG code snipped -->
  <image src="fallback.png" xlink:href="" />
</svg>
图像回退,它显示在 Internet Explorer 8 中

现在要解决那个剩余的回退问题:转储到屏幕上的 SVG 文本。它在某种程度上破坏了我们完美的回退图像替换。我们可以使用 CSS 来帮助我们解决这个问题,但这并不直接。我们不能使用简单的svg text { display: none; },因为这会破坏 SVG 图形。您可以使用检测 JavaScript 来设置类,这些类将根据需要隐藏或显示文本。但即使这样,您也会被 IE8 对无法识别的标记的非标准方法所阻碍:它们始终被视为没有子内容的空 void 标记。如果您想将它们视为可样式化的容器,则需要调整HTML5 Shiv代码以包含所有 SVG 标记。

一个不使用 JavaScript 的选项是将所有 SVG 图形代码(除了回退<image>)包装在<a>元素中。如果没有超链接目标,SVG 中的<a>元素的行为与<g>分组元素完全相同,因此对您的 SVG 代码没有影响。由于 IE8 将<a>识别为有效的容器元素,因此应用于它的任何样式都将应用于它内部的所有文本。

因此,技巧是应用样式以隐藏 HTML 链接的内容,但对 SVG 组没有影响。绝对定位和隐藏溢出可以解决这个问题

.hide-on-fallback {
  display: block;
  position: absolute;
  left: -100%;
  height: 0;
  width: 0;
  overflow: hidden;
}
图像回退,没有多余的文本,在 Internet Explorer 8 中。

SVG for Everybody是一个用于内联 SVG 回退的 JavaScript 库。它的方法是从最现代的内联 SVG 开始:通过<use>引用的内联 SVG,指向外部文件中定义的符号。

<svg role="img" title="CodePen">
  <use xlink:href="spritemap.svg#codepen"></use>
</svg>

如果支持,它将什么也不做。在 IE 的情况下,它使用 Ajax 请求 spritemap 并注入它,使其在现代 IE 中工作,而通常它无法工作。在不支持 SVG 的浏览器的情况下,它可以帮助您注入 PNG。

它唯一的缺点是它执行的 UA 测试不太准确。您可能最好使用 Ajax 请求 SVG并始终注入它,这将带来良好的支持和浏览器缓存。


关于图标系统的说明

拥有一个图标系统的原因是它使图标高效且易于使用。SVG 是一个很棒的图标系统。您可能希望图标系统的回退仍然既高效又易于使用。这意味着像<image>技巧这样的东西将是一个非常糟糕的回退。它使标记变得复杂,意味着每个单独的图标都是一个单独的请求。

您可能希望使用以下方法之一来处理图标系统回退

  • 使用背景图像作为回退,但让它们成为CSS 雪碧,这样它仍然只是一个请求。
  • 使用图标字体作为回退。您将需要一个空元素(建议的标记),但 @font-face 比 SVG 工作得更远,因此它可以作为一个很好的回退。
  • 使用Grunticon,它让您从一个空元素开始,并逐步增强到 SVG,并处理回退(通过 JavaScript 测试)。如果您愿意,您也可以使用 Grunticon,但仍然从内联 SVG 开始,如本教程中所述

通常,任何这些回退都需要一些 JavaScript 才能工作。如果您目标是使用 SVG 图标系统回退,但无需 JavaScript(在 Android 2.2/2.3 中有效),以下是一种方法。

这需要一些额外的标记(<a>除了<use>之外)

<svg class="icon">
  <a class="svg-status"></a>
  <use xlink:href="#svg-status" />
</svg>

然后在 CSS 中

.icon a, .icon + a {
  /* styles for every icon */
}
.icon .svg-status,
.icon + .svg-status {
  /* styles for this particular icon */
}

第一个选择器用于 Android,第二个用于 IE。使用兄弟选择器是因为旧版 IE 不会将无法识别的元素视为容器。

在 Internet Explorer 7 中,SVG 内部的锚点上的背景图像

如果您不喜欢额外的锚点元素,并且不介意在禁用 JavaScript 时丢失回退,另一种策略是使用 JavaScript 注入一个元素 (),旧版 Android 和旧版 IE 浏览器都可以对其进行样式设置。

该脚本通过查看带有标签名称“use”的元素是否具有 ownerSVGElement 属性 来测试内联 SVG 支持。使用您的图标 id 值(来自 <use> 元素的 xlink:href 属性)作为新跨度的类名,因此不需要额外的标记。

结论

您可以完全为 SVG 做回退。