我该使用<img>、<object>还是<embed>来插入SVG文件?

767

在加载SVG文件时,我应该使用<img><object>还是<embed>标签,以类似于加载jpggifpng的方式加载?

要确保每个标签的代码能够尽可能地正常工作,其中需要包括mimetype或指向回退SVG渲染器的引用。(在我的研究中,我看到了一些关于这些方面的参考资料,但没有看到一份好的现代参考文献。)

假设我正在使用Modernizr检查是否支持SVG,并针对不支持SVG的浏览器进行后备处理(可能会使用纯<img>标签替换)。


3
请访问 https://github.com/jonathantneal/svg4everybody 查看。 - Machado
3
理论上,您也可以拥有一个 <svg>,其中您想引用其他SVG。这可以通过使用 <image> 来实现。 - phk
2
我刚写了一篇关于这个问题的博客文章:https://claude-e-e.medium.com/ways-to-use-svg-in-your-html-page-dd504660cb37 - Claude
1
你可以自己加载外部的SVG文件,并将其注入到DOM中。可以参考我写的**<load-file> Web组件**,从中获得灵感。 - Danny '365CSI' Engelman
20个回答

755
我可以推荐SVG Primer(由W3C发布),它涵盖了这个主题:http://www.w3.org/Graphics/SVG/IG/resources/svgprimer.html#SVG_in_HTML 如果您使用<object>,那么您将自动获得位图回退(*免费)。
<object data="your.svg" type="image/svg+xml">
  <img src="yourfallback.jpg" />
</object>
*) 嗯,不完全是免费的,因为某些浏览器会下载这两个资源,可以参考Larry下面的建议来避免这种情况。 2014年更新:
  • 如果您想要一个非交互式的 SVG,请使用 <img> 并且提供脚本回退到 png 版本(适用于旧版IE和Android < 3)。一种干净简单的方法是:
  • <img src="your.svg" onerror="this.src='your.png'">

    它的表现和 GIF 图片非常相似,如果您的浏览器支持声明式动画(SMIL),则这些动画也将播放。

  • 如果您需要一个交互式的 SVG,请使用<iframe><object>
  • 如果您需要提供给旧版浏览器使用 SVG 插件的功能,则可以使用<embed>
  • 对于在CSS的背景图像中使用SVG的情况,可以选择使用Modernizr来切换到回退图像,另一个选择是依靠多个背景自动处理:
  • div {
        background-image: url(fallback.png);
        background-image: url(your.svg), none;
    }
    

    注意:多重背景策略在Android 2.3上不起作用,因为它支持多重背景但不支持SVG。

另一个很好的阅读材料是关于SVG回退的这篇博客文章


9
设置尺寸的正确方式是什么?我应该包含高度和宽度属性,还是使用CSS? - artlung
6
可以使用CSS,或在嵌入元素(即iframe、embed、object、img之一)上设置尺寸。后者的作用是在定义尺寸的样式表加载之前可能避免未经样式修饰的内容闪现问题。同时,请确保SVG具有viewBox属性,并从SVG根元素中删除width/height属性。以我的经验来看,这将为您带来最佳的跨浏览器行为。 - Erik Dahlström
8
该方法将始终下载光栅文件。此答案确保仅在旧版IE浏览器上请求回退。 - Larry
6
我认为,人们越快停止支持古老的浏览器,我们就能越快建立起一个保持浏览器更新的社会,甚至可以自动升级到2012年的浏览器。唯一的借口是没有免费获得现代浏览器,但例如Firefox仍然支持Windows XP(可能到版本52)。总是有现代且免费的替代方案。 - Neonit
3
值得注意的是(至少在 Chrome 上),使用<object>标签不会缓存SVG文件,而使用<img>标签则会缓存该文件。 - Métoule
显示剩余23条评论

154

54
如果SVG需要加载其他资源(如字体、图片等),那么这种方法就行不通了。 - TheHippo
16
为了语义化的原因,对于标志或图标,我仍然建议使用IMG标签,并确保SVG只是矢量插图。 - Christian Landgren
5
现在已经被广泛接受。@artlung,你可能想把你的回答改成这个。 - SteeveDroz
29
使用<img/>标签的人可能想了解如何添加SVG Fragment identifiers,例如"<img src="myimage.svg#svgView(preserveAspectRatio(none))" />",这使您可以像使用内联SVG定义一样控制纵横比、viewBox等。更多关于缩放的阅读 - https://css-tricks.com/scale-svg/ - brichins
谢谢,但是如何将现有的SVG图像添加到<svg>标签中? - Richard
使用img标签有一个缺点,即无法从外部操纵SVG。 - aderchox

123

<object><embed>具有一个有趣的属性:它们使得从外部文档(考虑同源策略)获取对SVG文档的引用成为可能。然后可以使用该引用来对SVG进行动画处理、更改其样式表等。

假设:

<object id="svg1" data="/static/image.svg" type="image/svg+xml"></object>

您随后可以执行以下操作:

document.getElementById("svg1").addEventListener("load", function() {
    var doc = this.getSVGDocument();
    var rect = doc.querySelector("rect"); // suppose our image contains a <rect>
    rect.setAttribute("fill", "green");
});

1
我认为你不会从<object>元素获得“load”事件。不支持。 - Stephen O'Connor
8
我不确定是否有充分的文档支持,但在Firefox、IE11和Chrome中它确实有效。至少对于外部SVG文件是这样:我无法用数据URI使其工作。 - WGH
非常好!有趣的是,当您有两个SVG文件时,添加“id”字段似乎是必需的,否则我的Opera浏览器只会显示1个? - Bram
使用这种方法,您既可以获得内联的优点,也可以获得引用的优点! - brandito
值得注意的是,除非SVG是内联的,否则SVG内部的超链接将无法正常工作。 - Mentalist
但它仍然无法使用CSS选择器进行访问。 - aderchox

39

使用 srcset

大多数现代浏览器已支持srcset属性,该属性允许为不同的用户指定不同的图像。例如,您可以将其用于1x和2x像素密度,浏览器将选择正确的文件。

在这种情况下,如果在srcset中指定了SVG而浏览器不支持它,则它会退回到src上。

<img src="logo.png" srcset="logo.svg" alt="My logo">

相较于其他解决方案,此方法有以下几个优点:

  1. 不依赖任何奇怪的黑科技或脚本
  2. 简单易行
  3. 您仍然可以包含alt文本
  4. 支持srcset的浏览器应该知道如何处理它,因此它只下载需要的文件。

15
现在几乎所有的浏览器都支持SVG,概率高达99%以上,那还有必要费心去考虑它的兼容性吗? - Robert Longson
5
除了确保每个人都可以准确地查看页面之外,Google也会更喜欢你! - Hoots
2
鉴于当前时刻您将备用图像推送给约10%的用户,因此看起来比实际情况要好。 - Volker E.
2
@VolkerE. 10%?caniuse显示缺乏SVG支持[约2%](https://caniuse.com/#search=svg)。尽管如此,我仍然像答案中一样使用srcset来支持这2%。将来,我希望浏览器能够根据性能和大小等因素获取类似于srcset的信息,并选择适当的图像,而不仅仅是设备大小或文件格式支持。 - Scribblemacher
@Scribblemacher 当我留下上面的评论时,我还在答案中添加了一个caniuse链接。截至今天,全球89.6%的srcset支持率。 - Volker E.
5
唯一的问题在于,当浏览器支持SVG但不支持srcset时,比如IE11,它会默认回退到PNG格式,即使它也能支持SVG。 - AshotN

28

个人建议使用<svg>标签,因为这样您可以完全控制它。如果您将其用于<img>,则无法使用CSS等来控制SVG的内部。

另一件事是浏览器支持问题。

只需打开svg文件并将其直接粘贴到模板中即可。

<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3400 2700" preserveAspectRatio="xMidYMid meet" (click)="goHome();">
  <g id="layer101">
    <path d="M1410 2283 c-162 -225 -328 -455 -370 -513 -422 -579 -473 -654 -486 -715 -7 -33 -50 -247 -94 -475 -44 -228 -88 -448 -96 -488 -9 -40 -14 -75 -11 -78 2 -3 87 85 188 196 165 180 189 202 231 215 25 7 129 34 230 60 100 26 184 48 185 49 4 4 43 197 43 212 0 10 -7 13 -22 9 -13 -3 -106 -25 -208 -49 -102 -25 -201 -47 -221 -51 l-37 -7 8 42 c4 23 12 45 16 49 5 4 114 32 243 62 129 30 240 59 246 66 10 10 30 132 22 139 -1 2 -110 -24 -241 -57 -131 -33 -240 -58 -242 -56 -6 6 13 98 22 107 5 4 135 40 289 80 239 61 284 75 307 98 14 15 83 90 153 167 70 77 132 140 139 140 7 0 70 -63 141 -140 70 -77 137 -150 150 -163 17 -19 81 -39 310 -97 159 -41 292 -78 296 -82 8 -9 29 -106 24 -111 -1 -2 -112 24 -245 58 -134 33 -245 58 -248 56 -6 -7 16 -128 25 -136 5 -4 112 -30 238 -59 127 -29 237 -54 246 -57 11 -3 20 -23 27 -57 6 -28 9 -53 8 -54 -1 -1 -38 7 -81 17 -274 66 -379 90 -395 90 -16 0 -16 -6 3 -102 11 -57 21 -104 22 -106 1 -1 96 -27 211 -57 115 -31 220 -60 234 -66 14 -6 104 -101 200 -211 95 -111 175 -197 177 -192 1 5 -40 249 -91 542 l-94 532 -145 203 c-220 309 -446 627 -732 1030 -143 201 -265 366 -271 367 -6 0 -143 -183 -304 -407z m10 -819 l-91 -161 -209 -52 c-115 -29 -214 -51 -219 -49 -6 1 32 55 84 118 l95 115 213 101 c116 55 213 98 215 94 1 -3 -38 -78 -88 -166z m691 77 l214 -99 102 -123 c56 -68 100 -125 99 -127 -4 -3 -435 106 -447 114 -4 2 -37 59 -74 126 -38 68 -79 142 -93 166 -13 23 -22 42 -20 42 2 0 101 -44 219 -99z"/>
    <path d="M1126 2474 c-198 -79 -361 -146 -363 -147 -2 -3 -70 -410 -133 -805 -12 -73 -20 -137 -18 -143 2 -6 26 23 54 63 27 40 224 320 437 622 213 302 386 550 385 551 -2 2 -165 -62 -362 -141z"/>
    <path d="M1982 2549 c25 -35 159 -230 298 -434 139 -203 283 -413 319 -465 37 -52 93 -134 125 -182 59 -87 83 -109 73 -65 -5 20 -50 263 -138 747 -17 91 -36 170 -42 176 -9 8 -571 246 -661 280 -14 6 -7 -10 26 -57z"/>
    <path d="M1679 1291 c-8 -11 -71 -80 -141 -153 l-127 -134 -95 -439 c-52 -242 -92 -442 -90 -445 6 -5 38 28 218 223 l99 107 154 0 c85 0 163 -4 173 -10 10 -5 78 -79 151 -162 73 -84 136 -157 140 -162 18 -21 18 4 -2 85 -11 46 -58 248 -105 448 l-84 364 -87 96 c-108 121 -183 201 -187 201 -2 0 -10 -9 -17 -19z m96 -488 c33 -102 59 -189 57 -192 -2 -6 -244 -2 -251 4 -5 6 120 375 127 375 4 0 34 -84 67 -187z"/>
    </g>
  </svg>

那么在你的CSS中,你可以简单地如下操作:

then in your css you can simply eg:

svg {
  fill: red;
}

一些资源:SVG技巧


不适用于必须使用外部(svg)文件工作的情况,这个问题是关于这个的。 - vsync

28
如果您希望您的SVG可以通过CSS样式进行完全控制,则必须在DOM中内联显示SVG。可以通过SVG注入来实现这一点,它使用JavaScript替换HTML元素(通常是<img>元素),在页面加载后将其替换为SVG文件的内容。

以下是使用SVGInject的最简示例:

<html>
<head>
  <script src="svg-inject.min.js"></script>
</head>
<body>
  <img src="image.svg" onload="SVGInject(this)" />
</body>
</html>

图像加载后,onload="SVGInject(this)将触发注入操作,<img>元素将被提供在src属性中的文件内容替换。这适用于所有支持SVG的浏览器。

免责声明:我是SVGInject的共同作者。


我仍然不明白使用SVG注入的优势。你能详细说明一下吗? - Ali Mert Çakar
4
你可以将SVG放在单独的文件中,但仍然可以使用CSS和/或JavaScript访问它们。在某些情况下,您可以使用webpack将SVG嵌入到文件中,但如果您需要动态访问SVG,则SVG注入基本上是唯一的方法来实现这一点。 - Waruyama
不幸的是,SVG_INJECT仅支持Angular 13或更低版本,因此我不得不转向https://github.com/czeckd/angular-svg-icon。 - JalilIrfan

27

最好的选择是在不同设备上使用SVG图像 :)

<img src="your-svg-image.svg" alt="Your Logo Alt" onerror="this.src='your-alternative-image.png'">

3
有趣。但你能保证 onerror 会在 SVG 无法渲染时触发吗?你测试过哪些浏览器? - artlung
1
是的,我在移动Android操作系统上测试了它,使用默认浏览器 :) - Roberth Solís

19

如果您使用<img>标签,那么基于webkit的浏览器将无法显示嵌入的位图图像

对于任何高级的SVG使用,包括SVG内联,都提供了最大的灵活性。

Internet Explorer和Edge会正确调整SVG的大小,但您必须指定高度和宽度。

您可以在SVG中的任何形状中添加onclick、onmouseover等,例如:onmouseover="top.myfunction(evt);"

您还可以通过将其包含在常规样式表中来在SVG中使用Web字体

注意:如果您从Illustrator导出SVG,则Web字体名称将不正确。 您可以在CSS中进行更正,避免在SVG中操作。例如,Illustrator将Arial的名称弄错了,您可以像这样进行修复:

@font-face {    
    font-family: 'ArialMT';    
    src:    
        local('Arial'),    
        local('Arial MT'),    
        local('Arial Regular');    
    font-weight: normal;    
    font-style: normal;    
}

所有这些功能都适用于自2013年以来发布的任何浏览器

例如,请参阅ozake.com整个网站都是由SVG制成的,除了联系表单。

警告:在Safari中,Web字体的大小调整不够精确 - 如果您在普通文本和加粗或斜体之间有很多转换,则转换点可能会留有一小部分额外或缺失的空间。有关更多信息,请参见我在此问题中的答案。


1
谢谢。刚刚花了3个小时努力解决光栅图像在我的“img”标签中未显示的问题...你知道这个问题是否有官方文档记录吗? - ryebread
1
@Andrew Swift,你在http://dev.ozake.com上获得了非常好的视觉效果,但是存在严重的可访问性问题:搜索引擎可能很难索引该网站(我不确定它们是否会捕捉SVG事件中隐藏的链接);该网站无法通过键盘导航;似乎屏幕阅读器也无法正常使用... - interDist

16
以下是我找到的所有将SVG插入HTML模板的方法以及它们之间的区别和主要优缺点的总结: ((( 1 )))
/* in CSS */
background-image: url(happy.svg);

这种方式既不允许JS交互,也无法使CSS对SVG元素有更精细的控制。

((( 2 )))

<img src="happy.svg" />

就像 ((( 1 ))) 一样,都不允许 JS 交互,也不能对 SVG 图形的各部分进行更精细的 CSS 控制。

如果您只需要一个静态的 SVG,则方法 ((( 1 ))) 和 ((( 2 ))) 都可以。

注意:使用 <img> 标签时,如果在 srcset 属性中指定了 SVG 并且浏览器不支持它,它将自动退回到 src 属性。

<img src="logo.png" srcset="logo.svg" alt="my logo">

((( 3 )))

<div>
 <!-- Paste the SVG code directly here. -->
 <svg>...</svg>
 <!-- AKA an inline svg -->
 <!-- would be the same if is created and appended using JavaScript. -->
</div>

该方法不具备(((1)))和(((2)))中提到的任何注意事项,但该方法会使模板代码混乱,并且SVG将被复制粘贴,因此它们将不在自己的单独文件中。 (((4)))
<object data="happy.svg" width="300" height="300"></object>

或:

<object data="your.svg" type="image/svg+xml">
  <img src="yourfallback.jpg" />
</object>

使用这种方法,SVG将无法使用外部CSS进行访问,但可以使用JavaScript进行访问。

((( 5 )))

使用iconfu/svg-inject将SVG文件保留在它们自己的单独文件中(以保持模板代码更加清晰),现在使用<img>标签添加SVG,svg-inject将自动将它们转换为内联SVG,因此它们既可用于CSS也可用于JavaScript。
注意1:如果使用JavaScript动态添加img,此方法也适用。
注意2:使用此方法添加的SVG也会像图像一样被浏览器缓存。

((( 6 )))

使用单个SVG精灵文件,然后使用<use>标签插入它们。这种方式也非常灵活,与((( 5 )))具有相同的效果。在视频中展示了这种方法(以及其他几种方法)。

((( 7 )))

(React特定方式)将它们转换为React组件(或编写一个加载它们的组件)。

((( 8 )))

<embed src="happy.svg" />

根据MDN的说法,大多数现代浏览器已经弃用并删除了对浏览器插件的支持。 这意味着如果您希望您的网站在普通用户的浏览器上可操作,则通常不应该依赖于<embed>


1
我认为(5)方法是清晰且动画化的SVG最佳选择。 - Flamingo

12

我的看法是:截至2019年,使用中的浏览器中有93%(每个浏览器的最后两个版本都是100%)可以处理<img>元素中的SVG:

enter image description here

来源: Can I Use

因此,我们可以说没有理由再使用<object>了。

然而,它仍然有优点

  • 在检查(例如使用Chrome Dev Tools)时,如果您想稍微篡改一下并查看实时更改,则会呈现整个SVG标记。

  • 如果您的浏览器不支持SVG,它提供了非常强大的备用实现,即使找不到SVG也可以工作。这是XHTML2规范的关键功能,就像Betamax或HD-DVD一样

但是也有缺点


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接