Cross-browser Responsive SVG with PNG fallback

Working with SVG is fun but putting it in action can be pain. There were two challenges that I faced; It is not supported by IE8 and below and It was not responsive enough on IE9 and above.

Internet Explorer is main culprit in cross-browser Responsive SVG. If we do not mention the height then IE assume the height of SVG as 150px by default and therefore the code max-width: 100%; height: auto failed. The SVG get stretched to fill the width but the height remains 150px.

I know there are so many solutions available and there is a buzz about SVG in internet but I did not find any particular solution satisfying my need. This prompted me to explore possibilities myself. I found a brilliant approach in a post Make SVG Responsive by Sara Soueidan. The trick is to wrap the SVG in an inline-block element and set the padding of parent element to the width/height ratio of SVG, so that the inline-block element always remain in same aspect ratio as SVG. And then set the position of SVG element to <code>absolute </code> relative to parent element. Long-story-short that she was able to achieve the desired result, and you can read in details about the approach in her post. This CSS-only trick works well if you know the aspect ratio of SVG, however it will not fit in if I try to make generalized solution without some JavaScript.

Writing a code to generalize the solution by Sara Soueidan had one challenge; get aspect ratio of SVG. To get that I needed the content access of external SVG, and that was possible if I load SVG through <object> tag.

Let make SVG responsive in all browsers

To make SVG responsive we need HTML markup, SVG with proper attributes and some lines JavaScript

SVG

Following are bare minimum attributes to make SVG responsive, replace $width, $height with actual width and height of SVG.

<svg

   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   width="100%"
   height="100%"
   preserveAspectRatio="xMinYMin meet"
   viewBox="0 0 $width $height"
   version="1.1"">
</svg>

HTML markup

As I said, we will be sing <object> tag to load external SVG using a class="svg" selector to identify it with our JavaScript:

<object class="svg" data="flower/flower.svg"  type="image/svg+xml" ></object> 

JavaScript

We need to put this JavaScript code in the bottom of our HTML.

The code will load a fallback image in non-SVG browsers if provided as data-fallback, otherwise load a PNG version with same name as SVG file (for example ‘images/flower.svg’ will load ‘images/flower.png’)

If you need to support IE7 and below then you are required to use IE7 document.querySelector() polyfill

(function () {
"use strict";

/* perform actions only if Browser is Internet Explorer */
var re = new RegExp("MSIE|Trident");
if (re.exec(navigator.userAgent) != null) {
var objects = document.querySelectorAll('.svg');
var listenerLoad = function (e) {
var node = e.target;
var viewBox = node.getSVGDocument().rootElement.attributes.viewBox;
if (!viewBox) {
return;
};
viewBox = viewBox.value.split(' ');
if (node.attributes['data-wrapped']) {
node.parentNode.style.padding = '0 0 ' + (viewBox[3] * 100 / viewBox[2]) + '%';
} else {
var wrapper = document.createElement("span");
node.parentNode.insertBefore(wrapper, node);
node.parentNode.removeChild(node);
wrapper.appendChild(node);
wrapper.style.display = 'inline-block';
wrapper.style.overflow = 'hidden';
wrapper.style.padding = '0 0 ' + (viewBox[3] * 100 / viewBox[2]) + '%';
wrapper.style.position = 'relative';
wrapper.style.width = '100%';
wrapper.style.verticalAlign = 'middle';
node.style.display = 'inline-block';
node.style.left = '0';
node.style.position = 'absolute';
node.style.top = '0';
node.width = "100%";
node.height = "100%";
node.setAttribute('data-wrapped', 'true');
}
};

/* Test if browser supports SVG */
if (document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Shape", "1.1")) {
for (var i = 0, l = objects.length; i < l; i++) { objects[i].addEventListener("load", listenerLoad); } } else { /* Load fallback image if no SVG support */ for (var i = 0, l = objects.length; i < l; i++) { var img = document.createElement('img'); img.src = objects[i].attributes['data-fallback'] ? objects[i].attributes['data-fallback'].value : objects[i].attributes['data'].value.replace(/.svg$/, '.png'); img.style.width = "100%"; img.style.height = "auto"; objects[i].parentNode.replaceChild(img, objects[i]); } } } })(); [/highlight] If you think that above code to too big, here is the minified version 🙂 [highlight lang="javascript" title="Minified JavaScript"] (function(){if(null!=/MSIE|Trident/.exec(navigator.userAgent)){var b=document.querySelectorAll(".svg"),d=function(c){c=c.target;var a=c.getSVGDocument().rootElement.attributes.viewBox;if(a)if(a=a.value.split(" "),c.attributes["data-wrapped"])c.parentNode.style.padding="0 0 "+100*a[3]/a[2]+"%";else{var b=document.createElement("span");c.parentNode.insertBefore(b,c);c.parentNode.removeChild(c);b.appendChild(c);b.style.display="inline-block";b.style.overflow="hidden";b.style.padding="0 0 "+100*a[3]/ a[2]+"%";b.style.position="relative";b.style.width="100%";b.style.verticalAlign="middle";c.style.display="inline-block";c.style.left="0";c.style.position="absolute";c.style.top="0";c.width="100%";c.height="100%";c.setAttribute("data-wrapped","true")}};if(document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Shape","1.1"))for(var a=0,e=b.length;a

Leave a Reply

Your email address will not be published. Required fields are marked *