The purpose of this article, is a combination of my findings around the internet, searching and learning the best way of adding a responsive SVG image to a web page. At the same it should also have the ability to edit styles and animation from an external JavaScript file.
In general there are six ways to embed SVG files to your web page:
- As an image using the
<img>
element - As a background image in CSS
- As an object using the
<object>
element - As an iframe using an
<iframe>
element - Using the
<embed>
element - Inline using the
<svg>
element
Embedding an inline SVG
When embedding a SVG this way, style rules can be added anywhere in the document to target specific elements of the SVG. Unlike with the other embedding techniques, styles don’t need to be included between the opening and closing <svg>
tags to work. It can be styled from eg. an external CSS file.
The downside to this technique though, adding a “code island”, makes the source code look messy and add to the page size. In this format it can also not be cached and end up rendering the same amount of time with every visit to the page.
★ Embedding a SVG using the <object> element
To avoid the SVG “code island”, whilst using advanced features such as CSS and scripting, the HTML5 <object>
element is the best option to include an external SVG file.
Advantages to this method:
- Keep the source code clean
- Can be cached by the browser
- Can still access the SVG elements for styling and animation
- Straight forward to add a fallback image or text for ‘non-svg’ browsers, it will then display the fallback content between the opening and closing
<object>
tags (see example code below)
<object type="image/svg+xml" data="myImage.svg">
<div class="fallback-img"></div>
</object>
For browsers that do not support SVG, let it fall back to a raster version of the image. But instead of adding it with <img>,
instead add it as a background image via CSS. If you add the image directly using <img>
, browsers that support SVG will request both the SVG and the fallback image, resulting in an unnecessary extra HTTP request. With the example above, just add the following to the CSS file (read more about it in David Bushell’s primer to SVG on his blog):
.fallback-img {
background-image: url(path/to/fallback/myImage.png);
/* other styles here */
}
Adding CSS
Whilst embedding a SVG using the <object>
element, I found styling elements within the SVG via an external CSS file did not work. But it does however when you place the styles within the SVG element inside <style>
. The styles (and scripts for that matter) also need to be wrapped between <![CDATA[ ... ]]>
to avoid any parsing errors. Eg.:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 68 65">
<style type="text/css">
<![CDATA[
.block { fill: blue; }
]]>
</style>
<rect class="block" x="0" y="0" width="100" height="100" />
</svg>
Adding scripting
In this example I want to use jQuery animate() or even better Velocity.js, an animation engine with the same API as jQuery animate(). The thing is, using the <object>
element to display the SVG and then try selecting elements inside the SVG is not that straightforward. Reason being, jQuery do only understand the HTML DOM, but not the SVG DOM. To get around this, use vanilla JavaScript to wrap everything in a window.onload
function. Then select the element with document.getElementById()
functions and get the SVG document inside the Object tag using contentDocument
.
I found Ben Frain’s article, Select an SVG inside an <object> tag with JavaScript, with code examples very useful to get things going with this approach.
Making it responsive
I use the latest Adobe Illustrator to export the SVG, using the responsive option (also make sure to read Sara Souiedan’s article, Tips for Creating and Exporting Better SVGs for the Web). This delete the height
and width
attributes, that is necessary to make the image responsive. The viewBox
attribute however, need to stay as is.
Unfortunately, because of Internet Explorer, a bit more work needs to be done to make the <object>
element display the SVG responsive. First off, explicitly set the width of the <object>
to 100% in the CSS file. This works great for all modern browsers, except in IE, where the <object>
element always default to 150px high. I use the padding hack by Thierry Koblentz as a workaround. Originally applied making videos in an iframe
responsive.
Make sure the <object>
element is wrapped in a container.
<div class="object-container">
<object type="image/svg+xml" data="myImage.svg">
<div class="fallback-img"></div>
</object>
</div>
For the container, the following styles need to be applied with the formula indicated:
.object-container {
height: 0; /* Collapse the container's height */
width: width-value; /* Specify the width in percentage */
padding-top: (svg-height ÷ svg-width) × width-value;
/* The above padding formula makes sure the aspect ratio of the container equals that of the SVG graphic */
position: relative; /* Create positioning context for SVG */
}
With the height of the container collapsed and a huge bit of padding applied, this will push the <object>
element out the bottom of its container. To get it back up within its container, the <object>
need to be positioned absolutely. Hence also adding the position: relative
to the container. For styling the <object>
element inside the container, it will be something like this:
object {
height: 100%;
left: 0;
position: absolute;
top: 0;
width: 100%;
}
When using a “code island”…
For a different use case it might be better or necessary to add the SVG as a “code island” instead. If so, the above mentioned technique to make it responsive will still be necessary, but the <svg>
element becomes the absolute positioned element instead of <object>
. All the styles however can go into the external stylesheet. Animations from a JavaScript can also be from an external JS file, targeting elements directly by referencing them by class
or id
.
Leave a Reply