假设您正在构建一个 SVG 图标系统。您正在手动构建一个包含 符号 的 SVG sprite,或者使用像 IcoMoon 或 grunt-svgstore 这样的构建工具为您创建它。
您如何处理这个 sprite.svg 文件呢?
一种选择是在文档的顶部包含它,然后使用 <use>
标签进行引用。
...
</head>
<body>
<!-- include it here -->
<?php include_once("svg/sprite.svg"); ?>
...
<!-- use it here -->
<a href="/" class="logo">
<svg class="logo">
<use xlink:href="#logo" />
</svg>
</a>
这种方法有效,但它没有很好地利用缓存。如果网站进行 HTML 缓存,每个页面都将包含这个相同的大块 SVG 内容,这会增加文件大小。更不用说 HTML 解析器需要在每个页面加载时读取文档中的所有这些内容,然后再继续处理其他内容。
可能更好的方法是利用浏览器缓存,就像我们对所有其他资源所做的那样。我们可以通过 使我们的 <use>
标签链接到外部资源 来实现这一点。但是某些浏览器在处理这种情况时存在问题。主要是任何版本的 IE 和一些旧版本的 WebKit。
SVG for Everybody,我们在这里推荐过,大部分情况下都能很好地工作。但是,它无法通过其使用的 UserAgent 检测识别到某些浏览器。
另一种方法是始终通过 Ajax 加载 sprite(每次都加载),然后将其注入到页面中。这意味着您可以浏览器缓存该 SVG,并且它可以在任何支持内联 SVG 的地方正常工作。
假设您有一个完整的 SVG 文档,您正在通过 Ajax 加载(sprite.svg),实际上这有点棘手。您必须确保 SVG 具有正确的命名空间,然后再将其附加到页面上(感谢 Amelia)。幸运的是,我们可以利用通常负责此任务的 HTML 解析器。我们通过将 SVG 附加到一个 <div>
中,然后再将该 <div>
附加到页面上来实现这一点。
var ajax = new XMLHttpRequest();
ajax.open("GET", "svg/sprite.svg", true);
ajax.send();
ajax.onload = function(e) {
var div = document.createElement("div");
div.innerHTML = ajax.responseText;
document.body.insertBefore(div, document.body.childNodes[0]);
}
如果您使用 jQuery,它提供的回调数据已经格式化为 SVG 文档,因此您需要将其强制转换为字符串,然后再将其附加到 div,最终附加到页面。
$.get("svg/sprite.svg", function(data) {
var div = document.createElement("div");
div.innerHTML = new XMLSerializer().serializeToString(data.documentElement);
document.body.insertBefore(div, document.body.childNodes[0]);
});
请记住,当您使用这种方式时,只需使用标识符,而不是外部资源。
<svg class="icon" viewBox="0 0 100 100">
<use xlink:href="#shape-icon-1" />
</svg>
似乎在 IE 和 Android 中 运行良好

还要记住,SVG for Everybody 可以帮助您将标记转换为 <img src="...png">
,以便在不支持的浏览器中使用,因此,如果您认为这一点很重要,那么您需要自行处理。
在 Opera 中根本无法工作。
真的吗?看起来它可以工作。
大约一个月前写了一些类似的东西。我依赖 jQuery,但您显然可以用原生 JS 来实现。https://pathartl.me/inline-svg-ajax/
非常酷!
看起来它对每个 SVG 都发送了一个 Ajax 请求。SVG sprite 的理念是它是一个单一的请求。只是需要考虑的一点。
是的!我主要编写此代码是为了用于大型 SVG。或者更确切地说,像加载普通图像一样加载可使用 CSS 样式的 SVG。因此,同步加载它们。
有点困惑。所以您的意思是使用 Ajax,浏览器能够缓存 sprite.svg,因为浏览器将其视为文件,对吗?
此外,您当然也依赖 JS 来工作。如果它失败或被禁用,显然这将无法工作,但我想如果您只包含像图标这样的增强功能,这也没关系。
都正确。
例如,以下是如何确保 SVG 正确提供并从 .htaccess 缓存的。
我喜欢它!
我一直在使用 类似的东西 来加载由 gulp-svgstore 组合的 svg。唯一的区别是我在脚本标签之前插入内容。
感谢您对
innerHTML
技巧的关注,但这里不需要它。XMLHttpRequest
为您提供了一个完整的 XML 文档;您可以直接获取 SVG 节点并将其附加。(因为有效的独立 SVG 文件是 XML,这将比 HTML 文档具有更好的浏览器支持,HTML 文档是后来的扩展。)
唯一的技巧是,对于 Firefox 和 IE,您需要明确告诉它们响应将是文档,而不是纯文本/二进制数据。我认为
image/svg+xml
MIME 类型就足够了,但显然事实并非如此。基本的 JavaScript 代码如下
在这个示例中,我添加了几行额外的代码,在将 SVG 注入文档之前在其上设置一个类,并在出现文件访问问题时添加一个 try/catch 块。如果看到一个闪亮的金色矩形,则表示它正在工作,如果看到纯红色,则表示它没有工作。
http://codepen.io/AmeliaBR/pen/OPbrjy
谢谢,Amelia。你能帮我理清使用 AJAX 获取和加载多个 svg sprite 文件的最佳方法吗?
我惊讶地发现这可以使用异步请求来工作。如果在将 SVG 添加到页面之前解析了
<use>
会发生什么情况?喜欢它!
喜欢它!
可以用 HTML 5 实现吗?
嗯,有点棘手,但很有用!
我只是将
<object>
放入 HTML 中然后在加载后用 SVG 内容替换它
这样可以获得缓存和干净的标记,在我看来。
我编写了一个 辅助函数 来执行这些替换操作,如下所示
这看起来很简洁,但这是否意味着每个 SVG 都是分开的?这里更大的想法是单一请求。
我使用此技术加载了一些单独的文件,但这实际上并不重要。您可以加载一个 sprite,然后像您想要的那样使用它。
HTML
JS
刚刚快速测试了一下 - 似乎在所有浏览器(FF、Chrome、Safari、Opera、IE 9 – 11)中都能正常工作。
我偶然发现了一个使用此技术的令人讨厌的怪癖:无论它们放在哪里,Chrome(40)都将使用的符号(例如,
<use xlink:href="#symbol-id" />
)视为原始<svg>
标签的后代,包括该标签在 DOM 中的位置,这消除了根据use
标签出现的上下文重新设置相同 SVG 符号的不同填充样式的可能性。例如,这在 Chrome 中不起作用
SVG
HTML
CSS
在 Firefox 中有效,我还没有在其他浏览器中尝试过,但 Chrome 认为符号实际上不存在于
<use>
标签内,因此选择器.pink-logo .logo-part
实际上没有匹配项。小更新;看起来 Chrome 的行为符合 W3C 规范
http://www.w3.org/TR/SVG11/struct.html#UseElement
由于这会一直应用到 SVG 的内部,因此我能想到的唯一解决方法(对于需要选择和设置 SVG 内特定元素样式的情况)是使用 JS 从 DOM 中选择符号并将其直接附加到一个新的 SVG 元素,完全避免使用
<use>
。对于大多数情况,您几乎肯定最好只是为每个变体创建重复的符号。(悲伤的小号声响起。)