Search code examples
csssvgresponsive-designinternet-explorer-11

Responsive SVGs on InternetExplorer with SVG injection


To make SVGs responsive on our website with Internet Explorer 11 I am using the "Canvas Hack" by Nicholas Gallagher. This hack uses an extra canvas element to make use the SVG keeps the aspect ratio. The whole structure of the inline SVGs looks something like this.

HTML:

<div style="position:relative;width:100%;">
  <canvas width="256" height="256"></canvas>
  <svg viewBox="0 0 256 256" preserveAspectRatio="xMaxYMax meet">
    ...
  </svg>
</div>

CSS:

canvas {
    display: block;
    width: 100%;
    visibility: hidden;
}

svg {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
}

I am using SVGInject to make the SVGs inline, which means the SVGs are kept is separate files. Before SVG injection the HTML looks like this:

<div style="position:relative;width:100%;">
  <canvas width="256" height="256"></canvas>
  <img src="myimage.svg" onload="SVGInject(this)" />
</div>

This works well, but maintenance is very annoying, because for each SVG the values of width and height for the canvas must be manually set to match the SVG's aspect ratio. And since the SVGs are kept in separate files, I have to open the SVG every time to find out the aspect ratio.

So I was wondering, is this something that could automatically be done during injection?.

My ideas was to create a script that during injection somehow reads the SVG's aspect ratio from the viewBox attribute and then set the width and height for the canvas accordingly.


Solution

  • SVGInject provides the following hooks to the injection: beforeLoad, afterLoad, beforeInject and afterInject. In your case you can use afterInject to modify the SVG, its parent, siblings, etc..

    With using the afterInject hook, you can not only set the width and height attributes of the <canvas> element, but you can even check if Internet Explorer is running and only insert the canvas in that case. This will make your HTML much cleaner.

    Here is a script (using jQuery) that will add the canvas only on Internet Explorer. In the <head> add these lines (the one including svg-inject.js should alrady be in your code):

    <script src="svg-inject.js"></script>
    <script>
      SVGInject.setOptions({afterInject: function(img, svg) {
        if (/Trident|MSIE/.test(window.navigator.userAgent)) { // if Internet Explorer
          var $svg = $(svg);
          // Get width and height from viewBox attribute if available
          var viewBoxVals = ($svg.attr('viewBox') || '').split(/[\s,]+/);
          var width = parseInt(viewBoxVals[2]);
          var height = parseInt(viewBoxVals[3]);
          if (width > 0 && height > 0) {
            // Set position of parent div to relative
            $svg.parent().css({position: 'relative'});
            // Add canvas using width and height from viewBox
            $svg.before('<canvas height="' + height + '" width="' + width + '"' +
             'style="display: block; width: 100%; visibility: hidden;"></canvas>');
            // Set SVG attributes to make it fill space reserved by canvas.
            $svg.css({position: 'absolute', top: 0, left: 0});
          }
        }
      }})
    </script>
    

    After the SVG is injected, the script checks if Internet Explorer is running. If so, it extracts the width and height values from the viewBox attribute and inserts a canvas before the SVG. Also, the parent's and the SVG's attributes are set to make the SVG responsive.

    SVGs can then be simply added like this:

    <div style="width:100%;">
      <img src="myimage.svg" onload="SVGInject(this)" />
    </div>
    

    No need to add the canvas in your HTML, it will be automatically inserted on Internet Explorer (and only on Internet Explorer).